diff --git a/apps/frontend/src/components/analytics/analytics.component.tsx b/apps/frontend/src/components/analytics/analytics.component.tsx index 210c09f9..17e9435a 100644 --- a/apps/frontend/src/components/analytics/analytics.component.tsx +++ b/apps/frontend/src/components/analytics/analytics.component.tsx @@ -6,29 +6,14 @@ import { StarsTableComponent } from '@gitroom/frontend/components/analytics/star import useSWR from 'swr'; import { useFetch } from '@gitroom/helpers/utils/custom.fetch'; import { LoadingComponent } from '@gitroom/frontend/components/layout/loading'; -import clsx from 'clsx'; -import { useStateCallback } from '@gitroom/react/helpers/use.state.callback'; export const AnalyticsComponent: FC = () => { const fetch = useFetch(); - const [page, setPage] = useStateCallback(1); const load = useCallback(async (path: string) => { return await (await fetch(path)).json(); }, []); - const starsCallback = useCallback( - async (path: string) => { - return await ( - await fetch(path, { - body: JSON.stringify({ page }), - method: 'POST', - }) - ).json(); - }, - [page] - ); - const { isLoading: isLoadingAnalytics, data: analytics } = useSWR( '/analytics', load @@ -37,83 +22,16 @@ export const AnalyticsComponent: FC = () => { '/analytics/trending', load ); - const { - isLoading: isLoadingStars, - data: stars, - mutate, - } = useSWR('/analytics/stars', starsCallback); - const changePage = useCallback( - (type: 'increase' | 'decrease') => () => { - setPage(type === 'increase' ? page + 1 : page - 1, () => mutate()); - }, - [page, mutate] - ); - - if (isLoadingAnalytics || isLoadingTrending || isLoadingStars) { + if (isLoadingAnalytics || isLoadingTrending) { return ; } return (
- -
-
-
- - - -
-

Stars per day

-
- - - -
-
-
- {stars?.stars?.length ? ( - - ) : ( -
- Load your GitHub repository from settings to see analytics -
- )} -
-
+ +
{/*
*/} {/*

News Feed

*/} diff --git a/apps/frontend/src/components/analytics/stars.and.forks.interface.ts b/apps/frontend/src/components/analytics/stars.and.forks.interface.ts index ad380707..3360f3c9 100644 --- a/apps/frontend/src/components/analytics/stars.and.forks.interface.ts +++ b/apps/frontend/src/components/analytics/stars.and.forks.interface.ts @@ -19,5 +19,4 @@ export interface StarsAndForksInterface { last: string; predictions: string; }; - stars: Stars[]; } \ No newline at end of file diff --git a/apps/frontend/src/components/analytics/stars.table.component.tsx b/apps/frontend/src/components/analytics/stars.table.component.tsx index 36c5e23a..4af67f70 100644 --- a/apps/frontend/src/components/analytics/stars.table.component.tsx +++ b/apps/frontend/src/components/analytics/stars.table.component.tsx @@ -1,30 +1,231 @@ -import {FC} from "react"; -import {Stars} from "@gitroom/frontend/components/analytics/stars.and.forks.interface"; -import {UtcToLocalDateRender} from "../../../../../libraries/react-shared-libraries/src/helpers/utc.date.render"; +import { FC, useCallback, useEffect, useMemo } from 'react'; +import { UtcToLocalDateRender } from '@gitroom/react/helpers/utc.date.render'; +import { Button } from '@gitroom/react/form/button'; +import dayjs from 'dayjs'; +import Link from 'next/link'; +import { useRouter, useSearchParams } from 'next/navigation'; +import useSWR from 'swr'; +import clsx from 'clsx'; +import { useFetch } from '@gitroom/helpers/utils/custom.fetch'; -export const StarsTableComponent: FC<{stars: Stars[]}> = (props) => { - const {stars} = props; - return ( - +export const UpDown: FC<{ name: string; param: string }> = (props) => { + const { name, param } = props; + const router = useRouter(); + const searchParams = useSearchParams(); + + const state = useMemo(() => { + const newName = searchParams.get('key'); + const newState = searchParams.get('state'); + + if (newName != param) { + return 'none'; + } + + return newState as 'asc' | 'desc'; + }, [searchParams, name, param]); + + const changeStateUrl = useCallback( + (newState: string) => { + const query = + newState === 'none' ? `` : `?key=${param}&state=${newState}`; + router.replace(`/analytics${query}`); + }, + [state, param] + ); + + const changeState = useCallback(() => { + changeStateUrl( + state === 'none' ? 'desc' : state === 'desc' ? 'asc' : 'none' + ); + }, [state, param]); + + return ( +
+
{name}
+
+ {['none', 'asc'].indexOf(state) > -1 && ( + + + + )} + {['none', 'desc'].indexOf(state) > -1 && ( + + + + )} +
+
+ ); +}; + +export const StarsTableComponent = () => { + const fetch = useFetch(); + const router = useRouter(); + const searchParams = useSearchParams(); + const page = +(searchParams.get('page') || 1); + + const starsCallback = useCallback( + async (path: string) => { + const key = searchParams.get('key'); + const state = searchParams.get('state'); + const page = +(searchParams.get('page') || 1); + + return await ( + await fetch(path, { + body: JSON.stringify({ + page, + ...(key && state ? { key, state } : {}), + }), + method: 'POST', + }) + ).json(); + }, + [searchParams] + ); + + const { + isLoading: isLoadingStars, + data: stars, + mutate, + } = useSWR('/analytics/stars', starsCallback, { + revalidateOnMount: false, + }); + + useEffect(() => { + mutate(); + }, [searchParams]); + + const renderMediaLink = useCallback((date: string) => { + const local = dayjs.utc(date).local(); + const weekNumber = local.isoWeek(); + const year = local.year(); + return `/launches?week=${weekNumber}&year=${year}`; + }, []); + + const changePage = useCallback( + (type: 'increase' | 'decrease') => () => { + const key = searchParams.get('key'); + const state = searchParams.get('state'); + const page = +(searchParams.get('page') || 1); + + const newPage = type === 'increase' ? page + 1 : page - 1; + const keyAndState = key && state ? `&key=${key}&state=${state}` : ''; + router.replace(`/analytics?page=${newPage}${keyAndState}`, { + forceOptimisticNavigation: false, + }); + }, + [searchParams] + ); + + return ( +
+
+
+ + + +
+

Stars per day

+
+ + + +
+
+
+ {stars?.stars?.length ? ( +
- - - - - + + + + + - + - {stars.map(p => ( - - - - - + {stars?.stars?.map((p: any) => ( + + + + + + - ))} + ))} -
RepositoryDateTotalStars
+ + + + + + + + Media
{p.login}{p.totalStars}{p.stars}Media
{p.login} + + {p.totalStars}{p.stars} + + + +
- ) -} \ No newline at end of file + + ) : ( +
+ Load your GitHub repository from settings to see analytics +
+ )} +
+
+ ); +}; diff --git a/apps/frontend/src/components/billing/no.billing.component.tsx b/apps/frontend/src/components/billing/no.billing.component.tsx index 1aec28ea..3b055f0a 100644 --- a/apps/frontend/src/components/billing/no.billing.component.tsx +++ b/apps/frontend/src/components/billing/no.billing.component.tsx @@ -12,12 +12,10 @@ import ReactLoading from 'react-loading'; import { deleteDialog } from '@gitroom/react/helpers/delete.dialog'; import { useToaster } from '@gitroom/react/toaster/toaster'; import dayjs from 'dayjs'; -import utc from 'dayjs/plugin/utc'; import clsx from 'clsx'; import { pricing } from '@gitroom/nestjs-libraries/database/prisma/subscriptions/pricing'; -import {useRouter} from "next/navigation"; -import {FAQComponent} from "@gitroom/frontend/components/billing/faq.component"; -dayjs.extend(utc); +import { useRouter } from 'next/navigation'; +import { FAQComponent } from '@gitroom/frontend/components/billing/faq.component'; export interface Tiers { month: Array<{ diff --git a/apps/frontend/src/components/launches/calendar.context.tsx b/apps/frontend/src/components/launches/calendar.context.tsx index 019db9e3..d7f4dc0c 100644 --- a/apps/frontend/src/components/launches/calendar.context.tsx +++ b/apps/frontend/src/components/launches/calendar.context.tsx @@ -1,8 +1,5 @@ 'use client'; import 'reflect-metadata'; -import weekOfYear from 'dayjs/plugin/weekOfYear'; -import isoWeek from 'dayjs/plugin/isoWeek'; -import utc from 'dayjs/plugin/utc'; import { createContext, @@ -18,10 +15,7 @@ import dayjs from 'dayjs'; import useSWR, { useSWRConfig } from 'swr'; import { useFetch } from '@gitroom/helpers/utils/custom.fetch'; import { Post, Integration } from '@prisma/client'; - -dayjs.extend(weekOfYear); -dayjs.extend(isoWeek); -dayjs.extend(utc); +import {useRouter, useSearchParams} from 'next/navigation'; const CalendarContext = createContext({ currentWeek: dayjs().week(), @@ -49,6 +43,8 @@ export const CalendarWeekProvider: FC<{ const [internalData, setInternalData] = useState([] as any[]); const [trendings, setTrendings] = useState([]); const { mutate } = useSWRConfig(); + const searchParams = useSearchParams(); + const router = useRouter(); useEffect(() => { (async () => { @@ -57,13 +53,16 @@ export const CalendarWeekProvider: FC<{ }, []); const [filters, setFilters] = useState({ - currentWeek: dayjs().week(), - currentYear: dayjs().year(), + currentWeek: +(searchParams.get('week') || dayjs().week()), + currentYear: +(searchParams.get('year') || dayjs().year()), }); const setFiltersWrapper = useCallback( (filters: { currentWeek: number; currentYear: number }) => { setFilters(filters); + router.replace(`/launches?week=${filters.currentWeek}&year=${filters.currentYear}`, { + forceOptimisticNavigation: false, + }) setTimeout(() => { mutate('/posts'); }); diff --git a/apps/frontend/src/components/launches/calendar.tsx b/apps/frontend/src/components/launches/calendar.tsx index 4696bb2d..fbfe6d19 100644 --- a/apps/frontend/src/components/launches/calendar.tsx +++ b/apps/frontend/src/components/launches/calendar.tsx @@ -6,9 +6,6 @@ import { useCalendar, } from '@gitroom/frontend/components/launches/calendar.context'; import dayjs from 'dayjs'; -import isBetween from 'dayjs/plugin/isBetween'; -dayjs.extend(isBetween); - import { openModal, useModals } from '@mantine/modals'; import { AddEditModal } from '@gitroom/frontend/components/launches/add.edit.model'; import clsx from 'clsx'; diff --git a/apps/frontend/src/components/layout/layout.settings.tsx b/apps/frontend/src/components/layout/layout.settings.tsx index e2dbcfa1..ea31a87b 100644 --- a/apps/frontend/src/components/layout/layout.settings.tsx +++ b/apps/frontend/src/components/layout/layout.settings.tsx @@ -16,6 +16,16 @@ import NotificationComponent from '@gitroom/frontend/components/notifications/no import Link from 'next/link'; import useSWR from 'swr'; import { useFetch } from '@gitroom/helpers/utils/custom.fetch'; +import dayjs from "dayjs"; +import utc from "dayjs/plugin/utc"; +import weekOfYear from "dayjs/plugin/weekOfYear"; +import isoWeek from "dayjs/plugin/isoWeek"; +import isBetween from "dayjs/plugin/isBetween"; + +dayjs.extend(utc); +dayjs.extend(weekOfYear); +dayjs.extend(isoWeek); +dayjs.extend(isBetween); export const LayoutSettings = ({ children }: { children: ReactNode }) => { const fetch = useFetch(); diff --git a/libraries/nestjs-libraries/src/database/prisma/stars/stars.repository.ts b/libraries/nestjs-libraries/src/database/prisma/stars/stars.repository.ts index 0563725b..d3e2bb8c 100644 --- a/libraries/nestjs-libraries/src/database/prisma/stars/stars.repository.ts +++ b/libraries/nestjs-libraries/src/database/prisma/stars/stars.repository.ts @@ -130,7 +130,7 @@ export class StarsRepository { getStarsFilter(githubs: string[], starsFilter: StarsListDto) { return this._stars.model.star.findMany({ orderBy: { - [starsFilter.sortBy || 'date']: 'desc' + [starsFilter.key || 'date']: starsFilter.state || 'desc' }, where: { login: { diff --git a/libraries/nestjs-libraries/src/dtos/analytics/stars.list.dto.ts b/libraries/nestjs-libraries/src/dtos/analytics/stars.list.dto.ts index 0cd39e73..cc435f11 100644 --- a/libraries/nestjs-libraries/src/dtos/analytics/stars.list.dto.ts +++ b/libraries/nestjs-libraries/src/dtos/analytics/stars.list.dto.ts @@ -6,6 +6,10 @@ export class StarsListDto { page: number; @IsOptional() - @IsIn(['totalStars', 'stars', 'date']) - sortBy: 'date' | 'stars' | 'totalStars'; + @IsIn(['login', 'totalStars', 'stars', 'date']) + key: 'login' | 'date' | 'stars' | 'totalStars'; + + @IsOptional() + @IsIn(['desc', 'asc']) + state: 'desc' | 'asc'; } \ No newline at end of file