a tool for shared writing and social publishing

analytics date selector

+167 -43
+128 -2
app/lish/[did]/[publication]/dashboard/PublicationAnalytics.tsx
··· 1 + import { ArrowRightTiny } from "components/Icons/ArrowRightTiny"; 1 2 import { UpgradeContent } from "../UpgradeModal"; 3 + import { Popover } from "components/Popover"; 4 + import { DatePicker } from "components/DatePicker"; 5 + import { useState } from "react"; 6 + import { useLocalizedDate } from "src/hooks/useLocalizedDate"; 7 + import type { DateRange } from "react-day-picker"; 8 + import { usePublicationData } from "./PublicationSWRProvider"; 2 9 3 10 export const PublicationAnalytics = () => { 11 + let isPro = true; 12 + 13 + let { data: publication } = usePublicationData(); 14 + let [dateRange, setDateRange] = useState<DateRange>({ from: undefined }); 15 + 16 + if (!isPro) 17 + return ( 18 + <div className="sm:mx-auto pt-4 s"> 19 + <UpgradeContent /> 20 + </div> 21 + ); 22 + 4 23 return ( 5 - <div className="sm:mx-auto pt-4 s"> 6 - <UpgradeContent /> 24 + <div className="analytics"> 25 + <div className="analyticsViewCount"> 26 + <div className="flex justify-between items-center gap-2 pb-2 w-full"> 27 + <div className="flex gap-2 items-center"> 28 + <h3>Traffic</h3> 29 + <ArrowRightTiny /> <PostSelector /> 30 + </div> 31 + <DateRangeSelector 32 + dateRange={dateRange} 33 + setDateRange={setDateRange} 34 + pubStartDate={publication?.publication?.indexed_at} 35 + /> 36 + </div> 37 + <div className="aspect-video w-full border border-border" /> 38 + </div> 39 + 40 + {/*<div>subscriber count over time</div> 41 + <div>Top Referrers</div>*/} 7 42 </div> 8 43 ); 9 44 }; 45 + 46 + const PostSelector = () => { 47 + return <div>Total</div>; 48 + }; 49 + 50 + const DateRangeSelector = (props: { 51 + pubStartDate: string | undefined; 52 + dateRange: DateRange; 53 + setDateRange: (dateRange: DateRange) => void; 54 + }) => { 55 + let buttonClass = 56 + "rounded-md px-1 text-sm border border-accent-contrast text-accent-contrast"; 57 + 58 + let currentDate = new Date(); 59 + 60 + console.log("dateRange" + props.dateRange.from?.toISOString()); 61 + console.log("pubstart" + props.pubStartDate); 62 + 63 + let startDate = useLocalizedDate( 64 + props.dateRange.from?.toISOString() || 65 + props.pubStartDate || 66 + "2025-01-01T12:00:00.000Z", 67 + { 68 + month: "short", 69 + day: "numeric", 70 + }, 71 + ); 72 + 73 + let endDate = useLocalizedDate( 74 + (props.dateRange.to ?? currentDate).toISOString(), 75 + { 76 + month: "short", 77 + day: "numeric", 78 + }, 79 + ); 80 + 81 + function handleDateChange(range: DateRange | undefined) { 82 + if (range) props.setDateRange(range); 83 + } 84 + 85 + return ( 86 + <Popover 87 + className={"w-fit"} 88 + trigger={ 89 + <div className="text-tertiary ml-0"> 90 + {props.dateRange.from === undefined 91 + ? "All Time" 92 + : `${startDate} - ${endDate}`} 93 + </div> 94 + } 95 + > 96 + <div className="flex gap-2 pt-1"> 97 + <button 98 + onClick={() => { 99 + props.setDateRange({ from: undefined, to: undefined }); 100 + }} 101 + className={`${buttonClass}`} 102 + > 103 + All 104 + </button> 105 + <button 106 + onClick={() => { 107 + let from = new Date(); 108 + from.setDate(from.getDate() - 7); 109 + props.setDateRange({ from, to: new Date() }); 110 + }} 111 + className={`${buttonClass}`} 112 + > 113 + Last Week 114 + </button> 115 + <button 116 + onClick={() => { 117 + let from = new Date(); 118 + from.setMonth(from.getMonth() - 1); 119 + props.setDateRange({ from, to: new Date() }); 120 + }} 121 + className={`${buttonClass}`} 122 + > 123 + Last Month 124 + </button> 125 + </div> 126 + <hr className="my-2 border-border-light" /> 127 + <DatePicker 128 + mode="range" 129 + selected={props.dateRange} 130 + onSelect={handleDateChange} 131 + disabled={(date) => date > new Date()} 132 + /> 133 + </Popover> 134 + ); 135 + };
+1
components/Blocks/DateTimeBlock.tsx
··· 166 166 > 167 167 <div className="flex flex-col gap-3 "> 168 168 <DatePicker 169 + mode="single" 169 170 selected={dateFact ? selectedDate : undefined} 170 171 onSelect={handleDaySelect} 171 172 />
+37 -40
components/DatePicker.tsx
··· 1 - import { ChevronProps, DayPicker as ReactDayPicker } from "react-day-picker"; 1 + import { 2 + ChevronProps, 3 + DayPicker as ReactDayPicker, 4 + DayPickerProps, 5 + } from "react-day-picker"; 2 6 import { ArrowRightTiny } from "components/Icons/ArrowRightTiny"; 3 7 4 8 const CustomChevron = (props: ChevronProps) => { ··· 9 13 ); 10 14 }; 11 15 12 - interface DayPickerProps { 13 - selected: Date | undefined; 14 - onSelect: (date: Date | undefined) => void; 15 - disabled?: (date: Date) => boolean; 16 - } 16 + export const DatePicker = (props: DayPickerProps) => { 17 + const dayPickerProps = { 18 + ...props, 19 + required: props.required ?? false, 20 + mode: props.mode ?? "single", 21 + components: { 22 + Chevron: (chevronProps: ChevronProps) => ( 23 + <CustomChevron {...chevronProps} /> 24 + ), 25 + ...props.components, 26 + }, 27 + classNames: { 28 + months: "relative", 29 + month_caption: 30 + "font-bold text-center w-full bg-border-light mb-2 py-1 rounded-md", 31 + button_next: 32 + "absolute right-0 top-1 p-1 text-secondary hover:text-accent-contrast flex align-center", 33 + button_previous: 34 + "absolute left-0 top-1 p-1 text-secondary hover:text-accent-contrast rotate-180 flex align-center", 35 + chevron: "text-inherit", 36 + month_grid: "border-separate [border-spacing:2px]", 37 + weekdays: "text-secondary text-sm", 38 + selected: "bg-accent-1 text-accent-2 rounded-md font-bold", 39 + range_middle: "bg-[var(--accent-light)]!", 40 + day: "h-8! w-8! text-center rounded-md sm:hover:bg-border-light", 41 + outside: "text-tertiary", 42 + today: "font-bold", 43 + disabled: "text-border cursor-not-allowed hover:bg-transparent!", 44 + ...props.classNames, 45 + }, 46 + } as DayPickerProps; 17 47 18 - export const DatePicker = ({ 19 - selected, 20 - onSelect, 21 - disabled, 22 - }: DayPickerProps) => { 23 - return ( 24 - <ReactDayPicker 25 - components={{ 26 - Chevron: (props: ChevronProps) => <CustomChevron {...props} />, 27 - }} 28 - classNames={{ 29 - months: "relative", 30 - month_caption: 31 - "font-bold text-center w-full bg-border-light mb-2 py-1 rounded-md", 32 - button_next: 33 - "absolute right-0 top-1 p-1 text-secondary hover:text-accent-contrast flex align-center", 34 - button_previous: 35 - "absolute left-0 top-1 p-1 text-secondary hover:text-accent-contrast rotate-180 flex align-center", 36 - chevron: "text-inherit", 37 - month_grid: "w-full table-fixed", 38 - weekdays: "text-secondary text-sm", 39 - selected: "bg-accent-1! text-accent-2 rounded-md font-bold", 40 - day: "h-[34px] text-center rounded-md sm:hover:bg-border-light", 41 - outside: "text-tertiary", 42 - today: "font-bold", 43 - disabled: "text-border cursor-not-allowed hover:bg-transparent!", 44 - }} 45 - mode="single" 46 - selected={selected} 47 - defaultMonth={selected} 48 - onSelect={onSelect} 49 - disabled={disabled} 50 - /> 51 - ); 48 + return <ReactDayPicker {...dayPickerProps} />; 52 49 }; 53 50 54 51 export const TimePicker = (props: {
+1 -1
components/Pages/Backdater.tsx
··· 61 61 62 62 return ( 63 63 <Popover 64 - className="w-64 z-10 px-2!" 64 + className="w-fit z-10 px-2!" 65 65 trigger={ 66 66 <div className="underline"> 67 67 {timeAgo(localPublishedAt.toISOString())}