This commit is contained in:
2025-09-09 07:31:48 +00:00
parent 94b34d5946
commit 2171d5e744
3 changed files with 129 additions and 50 deletions

View File

@@ -1,4 +1,3 @@
import DayRange from "./svgs/dayRange";
import OneMonth from "./svgs/oneMonth";
import Realtime from "./svgs/realtime";
import Arrow from "./svgs/arrow";
@@ -32,12 +31,13 @@ export default function CalenderSelector( {dateString, is_small, onRangeChange}:
}}
>
<div className="flex-shrink-0">
{selected === DateRangeEnum.DAY_RANGE && <DayRange color="#848484" width={is_small ? 16 : 20} height={is_small ? 16 : 20} />}
{selected === DateRangeEnum.ONE_MONTH&& <OneMonth color="#848484" width={is_small ? 16 : 20} height={is_small ? 16 : 20} />}
{selected === DateRangeEnum.ALL&& <Realtime color="#848484" width={is_small ? 16 : 20} height={is_small ? 16 : 20} />}
{selected === DateRangeEnum.ALL ? (
<Realtime color="#848484" width={is_small ? 16 : 20} height={is_small ? 16 : 20} />
) : (
<OneMonth color="#848484" width={is_small ? 16 : 20} height={is_small ? 16 : 20} />
)}
</div>
<div className={`flex-1 min-w-0 font-semibold ${is_small ? 'text-xs':'text-sm'}`}>
{selected === DateRangeEnum.DAY_RANGE && `${rangeStart.toISOString().split('T')[0]} ~ ${rangeEnd.toISOString().split('T')[0]}`}
{selected === DateRangeEnum.ONE_MONTH&& '최근 1개월'}
{selected === DateRangeEnum.ONE_WEEK&& '최근 1주일'}
{selected === DateRangeEnum.TWO_MONTHS&& '최근 2개월'}
@@ -165,38 +165,7 @@ export default function CalenderSelector( {dateString, is_small, onRangeChange}:
<OneMonth color={selected === DateRangeEnum.ONE_YEAR ? '#F94B37' : '#848484'} width={16} height={16} />
<span className="text-sm"> 1</span>
</button>
<div
className={`flex items-center gap-1 px-2 pr-0 h-[32px] rounded-md hover:bg-[#f6f6f6] text-left ${selected === DateRangeEnum.DAY_RANGE ? 'bg-[#FFF3F2] font-semibold' : ''}`}
onClick={() => {
// setSelected(DateRangeEnum.DAY_RANGE);
// setIsOpen(false);
}}
>
<DayRange color={selected === DateRangeEnum.DAY_RANGE ? '#F94B37' : '#848484'} width={16} height={16} />
<input
type="text"
value={rangeStart.toISOString().split('T')[0]}
onChange={(e) => setRangeStart(new Date(e.target.value))}
className="border-1 border-border-pale rounded-md p-0.5 text-xs w-[84px] text-center"
/>
<span className="mx-0">~</span>
<input
type="text"
value={rangeEnd.toISOString().split('T')[0]}
onChange={(e) => setRangeEnd(new Date(e.target.value))}
className="border-1 border-border-pale rounded-md p-0.5 text-xs w-[84px] text-center"
/>
<button className="text-xs text-[#848484] border-1 border-border-pale rounded-md p-0.5 px-1 hover:text-[#F94B37] hover:border-[#F94B37]"
onClick={() => {
setSelected(DateRangeEnum.DAY_RANGE);
onRangeChange?.(rangeStart, rangeEnd);
setIsOpen(false);
}}
>
</button>
</div>
</div>
</div>
)}

View File

@@ -341,21 +341,20 @@ export default function Page({user}: {user: any}) {
expectedRevenue: { border: '#16A34A', bg: 'rgba(22, 163, 74, 0.25)' },
}
const selectedRows = useMemo(() => sortedVisibleRows.filter(r => checkedIds.has(r.id)), [sortedVisibleRows, checkedIds])
const [seriesMode, setSeriesMode] = useState<'items'|'series'>('items')
const [seriesInterval, setSeriesInterval] = useState<'day'|'week'|'month'>('day')
const [seriesItems, setSeriesItems] = useState<Array<{ period: string, views: number, validViews: number, premiumViews: number, expectedRevenue: number }>>([])
const chartData = useMemo(() => {
const col = metricColor[selectedMetric]
if (seriesMode === 'series') {
const labels = seriesItems.map(it => it.period)
const dataVals = seriesItems.map(it => (it as any)[selectedMetric] as number)
return { labels, datasets: [{ label: `${metricLabel[selectedMetric]} (${seriesInterval})`, data: dataVals, borderColor: col.border, backgroundColor: col.bg, tension: 0.35 }] }
}
const labels = selectedRows.map(r => r.subject)
const dataVals = selectedRows.map(r => (r as any)[selectedMetric] as number)
const col = metricColor[selectedMetric]
return {
labels,
datasets: [{
label: metricLabel[selectedMetric],
data: dataVals,
borderColor: col.border,
backgroundColor: col.bg,
tension: 0.35,
}]
}
}, [selectedRows, selectedMetric])
return { labels, datasets: [{ label: metricLabel[selectedMetric], data: dataVals, borderColor: col.border, backgroundColor: col.bg, tension: 0.35 }] }
}, [selectedRows, selectedMetric, seriesMode, seriesItems, seriesInterval])
const chartOptions = useMemo(() => ({
responsive: true,
plugins: { legend: { display: false }, title: { display: false } },
@@ -616,7 +615,28 @@ export default function Page({user}: {user: any}) {
</div>
<div className="border-1 hidden xl:col-[3/5] xl:row-[3/5] bg-white rounded-lg p-4 xl:flex xl:flex-col" style={{ borderColor: metricColor[selectedMetric].border }}>
<div className="text-xl font-bold"> {metricLabel[selectedMetric]} </div>
<div className="text-normal text-gray-500"> .</div>
<div className="text-normal text-gray-500 flex flex-row gap-2 items-center">
<select className="border-1 border-[#e6e9ef] rounded px-2 py-1" value={seriesMode} onChange={e => setSeriesMode(e.target.value as any)}>
<option value="items"></option>
<option value="series"></option>
</select>
{seriesMode === 'series' && (
<>
<select className="border-1 border-[#e6e9ef] rounded px-2 py-1" value={seriesInterval} onChange={e => setSeriesInterval(e.target.value as any)}>
<option value="day"></option>
<option value="week"></option>
<option value="month"></option>
</select>
<button className="border-1 border-[#e6e9ef] rounded px-2 py-1" onClick={async () => {
const s = startDate; const e = endDate;
const qs = new URLSearchParams({ start: s.toISOString(), end: e.toISOString(), mode: 'series', interval: seriesInterval })
const res = await fetch(`/api/contents/mycontent?${qs.toString()}`, { cache: 'no-store' })
const data = await res.json()
if (res.ok) setSeriesItems(data.items || [])
}}></button>
</>
)}
</div>
<div className="flex flex-row justify-start items-center">
<div className="font-bold text-3xl my-4"> {formatNumberWithCommas(
selectedMetric === 'views' ? totals.views :