moonhead 1.0.4 → 1.0.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,3 @@
1
+ export declare function CampaignDetailTabs({ hidePerformance, }: {
2
+ hidePerformance?: boolean;
3
+ }): import("react").JSX.Element;
@@ -0,0 +1 @@
1
+ "use client";import{jsx as e,jsxs as D}from"react/jsx-runtime";import{useTranslations as T}from"next-intl";import{useQueryStates as l}from"nuqs";import{useTransition as m}from"react";import{Tabs as f,TabsIndicator as p,TabsList as u,TabsTab as c}from"../../../components/ui/tabs.js";import{CAMPAIGN_DETAIL_TABS as g,campaignDetailTabParser as d}from"../list-params.js";function I({hidePerformance:t=!1}){const s=T("Campaigns.DetailTabs"),[i,n]=m(),[{tab:r},o]=l({tab:d},{shallow:!1,clearOnDefault:!0,startTransition:n}),b=g.filter(a=>!(t&&a==="performance"));return e(f,{value:r,onValueChange:a=>o({tab:a}),"data-pending":i?"":void 0,children:D(u,{children:[b.map(a=>e(c,{value:a,children:s(a)},a)),e(p,{})]})})}export{I as CampaignDetailTabs};
@@ -1 +1 @@
1
- "use client";import{Fragment as L,jsx as t,jsxs as l}from"react/jsx-runtime";import{IconSpeakerphone as f}from"@tabler/icons-react";import{useFormatter as x,useTranslations as D}from"next-intl";import{useCallback as S,useMemo as y,useRef as N}from"react";import{DataTable as k}from"../../../components/ui/data-table.js";import{Identity as T}from"../../../components/ui/identity.js";import{SortButton as h}from"../../../components/ui/sort-button.js";import{TablePagination as b}from"../../../components/ui/table-pagination.js";import{TextButton as A}from"../../../components/ui/text-link.js";import{CampaignRowActions as v}from"./campaign-row-actions.js";import{CampaignStatusBadge as I}from"./campaign-status-badge.js";import{TrackingLinkCell as z}from"./tracking-link-cell.js";import{getCommissionForDealView as B}from"../../commissions/actions.js";import{CommissionDetailDialog as P}from"../../commissions/components/commission-detail-dialog.js";import{useDeferredDialog as R}from"../../../lib/hooks/use-deferred-dialog.js";import{PAGE_SIZE_OPTIONS as E}from"../../../lib/list-params.js";function X({result:n,canManage:o,trackerBase:s}){const a=D("Campaigns"),m=x(),r=N(null),c=R(()=>r.current?B(r.current):Promise.resolve(null)),u=c.trigger,d=S(i=>{r.current=i,u()},[u]),g=o,C=y(()=>{const i=e=>m.dateTime(e,{day:"numeric",month:"short",year:"numeric"}),p=[{id:"name",header:()=>t(h,{column:"name",children:a("columnName")}),cell:({row:e})=>t(T,{visual:t("div",{className:"flex size-9 shrink-0 items-center justify-center rounded-full bg-muted text-muted-foreground",children:t(f,{size:18,"aria-hidden":!0})}),primary:e.original.name,secondary:e.original.description?t("span",{className:"line-clamp-1 text-xs text-muted-foreground",children:e.original.description}):null})},{id:"status",header:()=>a("columnStatus"),cell:({row:e})=>t(I,{status:e.original.status,dealStatus:e.original.dealStatus,commissionStatus:e.original.commissionStatus})},{id:"casino",header:()=>a("columnCasino"),cell:({row:e})=>t("span",{className:"text-sm",children:e.original.casinoName})},{id:"commission",header:()=>a("columnCommission"),cell:({row:e})=>t(A,{onClick:()=>d(e.original.commissionId),children:e.original.commissionTitle??a("untitledCommission")})},{id:"deal",header:()=>a("columnDeal"),cell:({row:e})=>e.original.dealStartsAt?l("span",{className:"text-sm text-muted-foreground tabular-nums",children:[i(e.original.dealStartsAt)," \u2013"," ",e.original.dealEndsAt?i(e.original.dealEndsAt):a("dealOpenEnded")]}):t("span",{className:"text-sm text-muted-foreground",children:"\u2014"})},{id:"createdAt",header:()=>t(h,{column:"createdAt",children:a("columnCreated")}),cell:({row:e})=>t("span",{className:"text-sm text-muted-foreground tabular-nums",children:i(e.original.createdAt)})},{id:"trackingLink",header:()=>a("columnTrackingLink"),cell:({row:e})=>t(z,{slug:e.original.slug,trackerBase:s}),meta:{className:"text-right"}}];return g?[...p,{id:"actions",header:()=>t("span",{className:"sr-only",children:a("columnActions")}),cell:({row:e})=>t(v,{campaignId:e.original.id,slug:e.original.slug,status:e.original.status,canManage:o,trackerBase:s}),meta:{pin:"right",className:"text-right"}}]:p},[a,m,o,g,s,d]);return l(L,{children:[t(k,{data:n.rows,columns:C,getRowId:i=>i.id,empty:l("div",{className:"flex flex-col items-center gap-3 py-4",children:[t("div",{className:"flex size-10 items-center justify-center rounded-full bg-muted text-muted-foreground",children:t(f,{size:18,"aria-hidden":!0})}),t("span",{children:a("emptyState")})]}),pagination:t(b,{page:n.page,pageSize:n.pageSize,totalPages:n.totalPages,pageSizeOptions:E})}),t(P,{controller:c})]})}export{X as CampaignsTable};
1
+ "use client";import{Fragment as O,jsx as t,jsxs as l}from"react/jsx-runtime";import{IconSpeakerphone as f}from"@tabler/icons-react";import{useFormatter as C,useTranslations as D}from"next-intl";import{useCallback as S,useMemo as k,useRef as y}from"react";import{DataTable as N}from"../../../components/ui/data-table.js";import{Identity as T}from"../../../components/ui/identity.js";import{SortButton as h}from"../../../components/ui/sort-button.js";import{TablePagination as b}from"../../../components/ui/table-pagination.js";import{TextButton as A,TextLink as v}from"../../../components/ui/text-link.js";import{CampaignRowActions as I}from"./campaign-row-actions.js";import{CampaignStatusBadge as z}from"./campaign-status-badge.js";import{TrackingLinkCell as B}from"./tracking-link-cell.js";import{getCommissionForDealView as L}from"../../commissions/actions.js";import{CommissionDetailDialog as P}from"../../commissions/components/commission-detail-dialog.js";import{useDeferredDialog as R}from"../../../lib/hooks/use-deferred-dialog.js";import{PAGE_SIZE_OPTIONS as E}from"../../../lib/list-params.js";function X({result:n,canManage:o,trackerBase:s}){const a=D("Campaigns"),m=C(),r=y(null),c=R(()=>r.current?L(r.current):Promise.resolve(null)),u=c.trigger,d=S(i=>{r.current=i,u()},[u]),g=o,x=k(()=>{const i=e=>m.dateTime(e,{day:"numeric",month:"short",year:"numeric"}),p=[{id:"name",header:()=>t(h,{column:"name",children:a("columnName")}),cell:({row:e})=>t(T,{visual:t("div",{className:"flex size-9 shrink-0 items-center justify-center rounded-full bg-muted text-muted-foreground",children:t(f,{size:18,"aria-hidden":!0})}),primary:t(v,{href:`/campaigns/${e.original.id}`,tone:"plain",children:e.original.name}),secondary:e.original.description?t("span",{className:"line-clamp-1 text-xs text-muted-foreground",children:e.original.description}):null})},{id:"status",header:()=>a("columnStatus"),cell:({row:e})=>t(z,{status:e.original.status,dealStatus:e.original.dealStatus,commissionStatus:e.original.commissionStatus})},{id:"casino",header:()=>a("columnCasino"),cell:({row:e})=>t("span",{className:"text-sm",children:e.original.casinoName})},{id:"commission",header:()=>a("columnCommission"),cell:({row:e})=>t(A,{onClick:()=>d(e.original.commissionId),children:e.original.commissionTitle??a("untitledCommission")})},{id:"deal",header:()=>a("columnDeal"),cell:({row:e})=>e.original.dealStartsAt?l("span",{className:"text-sm text-muted-foreground tabular-nums",children:[i(e.original.dealStartsAt)," \u2013"," ",e.original.dealEndsAt?i(e.original.dealEndsAt):a("dealOpenEnded")]}):t("span",{className:"text-sm text-muted-foreground",children:"\u2014"})},{id:"createdAt",header:()=>t(h,{column:"createdAt",children:a("columnCreated")}),cell:({row:e})=>t("span",{className:"text-sm text-muted-foreground tabular-nums",children:i(e.original.createdAt)})},{id:"trackingLink",header:()=>a("columnTrackingLink"),cell:({row:e})=>t(B,{slug:e.original.slug,trackerBase:s}),meta:{className:"text-right"}}];return g?[...p,{id:"actions",header:()=>t("span",{className:"sr-only",children:a("columnActions")}),cell:({row:e})=>t(I,{campaignId:e.original.id,slug:e.original.slug,status:e.original.status,canManage:o,trackerBase:s}),meta:{pin:"right",className:"text-right"}}]:p},[a,m,o,g,s,d]);return l(O,{children:[t(N,{data:n.rows,columns:x,getRowId:i=>i.id,empty:l("div",{className:"flex flex-col items-center gap-3 py-4",children:[t("div",{className:"flex size-10 items-center justify-center rounded-full bg-muted text-muted-foreground",children:t(f,{size:18,"aria-hidden":!0})}),t("span",{children:a("emptyState")})]}),pagination:t(b,{page:n.page,pageSize:n.pageSize,totalPages:n.totalPages,pageSizeOptions:E})}),t(P,{controller:c})]})}export{X as CampaignsTable};
@@ -12,3 +12,15 @@ export declare const loadCampaignStatus: import("nuqs/server").LoaderFunction<{
12
12
  parseServerSide(value: string | string[] | undefined): NonNullable<"active" | "deactivated" | "all">;
13
13
  };
14
14
  }>;
15
+ export declare const CAMPAIGN_DETAIL_TABS: readonly ["overview", "performance"];
16
+ export type CampaignDetailTab = (typeof CAMPAIGN_DETAIL_TABS)[number];
17
+ export declare const campaignDetailTabParser: Omit<import("nuqs/server").SingleParserBuilder<"performance" | "overview">, "parseServerSide"> & {
18
+ readonly defaultValue: NonNullable<"performance" | "overview">;
19
+ parseServerSide(value: string | string[] | undefined): NonNullable<"performance" | "overview">;
20
+ };
21
+ export declare const loadCampaignDetailTab: import("nuqs/server").LoaderFunction<{
22
+ tab: Omit<import("nuqs/server").SingleParserBuilder<"performance" | "overview">, "parseServerSide"> & {
23
+ readonly defaultValue: NonNullable<"performance" | "overview">;
24
+ parseServerSide(value: string | string[] | undefined): NonNullable<"performance" | "overview">;
25
+ };
26
+ }>;
@@ -1 +1 @@
1
- import{createLoader as t,parseAsStringLiteral as a}from"nuqs/server";const p=["createdAt","name"],e=["all","active","deactivated"],r=a(e).withDefault("active"),s=t({status:r});export{p as CAMPAIGN_SORT_KEYS,e as CAMPAIGN_STATUSES,r as campaignStatusParser,s as loadCampaignStatus};
1
+ import{createLoader as t,parseAsStringLiteral as a}from"nuqs/server";const s=["createdAt","name"],e=["all","active","deactivated"],o=a(e).withDefault("active"),i=t({status:o}),r=["overview","performance"],p=a(r).withDefault("overview"),A=t({tab:p});export{r as CAMPAIGN_DETAIL_TABS,s as CAMPAIGN_SORT_KEYS,e as CAMPAIGN_STATUSES,p as campaignDetailTabParser,o as campaignStatusParser,A as loadCampaignDetailTab,i as loadCampaignStatus};
@@ -0,0 +1,8 @@
1
+ import "server-only";
2
+ import { type ConversionStats, type Delta, type MoneyByCurrency } from "./queries.js";
3
+ import { type MoneySeries, type PerformanceSeries } from "./series.js";
4
+ export declare const getCampaignConversionStats: (campaignId: string, startDate: string, endDate: string) => Promise<ConversionStats>;
5
+ export declare const getCampaignEarnings: (campaignId: string, startDate: string, endDate: string) => Promise<MoneyByCurrency[]>;
6
+ export declare const getCampaignClickStats: (campaignId: string, startDate: string, endDate: string) => Promise<Delta | null>;
7
+ export declare const getCampaignPerformanceSeries: (campaignId: string, startDate: string, endDate: string) => Promise<PerformanceSeries>;
8
+ export declare const getCampaignEarningsSeries: (campaignId: string, startDate: string, endDate: string) => Promise<MoneySeries>;
@@ -0,0 +1 @@
1
+ import"server-only";import{and as d,eq as m,gte as p,inArray as O,lt as v,ne as U,sum as B}from"drizzle-orm";import{unstable_cache as l}from"next/cache";import{db as f}from"../../db/index.js";import{CONVERSION_EVENT as A,conversions as e,EARNING_STATUS as R,earningsLedger as r}from"../../db/schema/index.js";import{clickHouseRows as L,clickHouseScalar as w}from"./clickhouse.js";import{countRows as g,windowBounds as y}from"./queries.js";import{assembleMoney as T,assemblePerformance as x,eachDay as I}from"./series.js";const S=60,W=l(async(s,n,t)=>{const{start:o,end:a,prevStart:c}=y(n,t),i=m(e.campaignId,s),u=d(i,p(e.receivedAt,o),v(e.receivedAt,a)),C=d(i,p(e.receivedAt,c),v(e.receivedAt,o)),E=m(e.event,A.ftd),P=m(e.event,A.registration),[N,M,b,h,k,D]=await Promise.all([g(e,u),g(e,C),g(e,d(E,u)),g(e,d(E,C)),g(e,d(P,u)),g(e,d(P,C))]);return{conversions:{current:N,previous:M},ftd:{current:b,previous:h},registrations:{current:k,previous:D}}},["dashboard:campaign-conversion-stats"],{revalidate:S}),j=l(async(s,n,t)=>{const{start:o,end:a}=y(n,t);return(await f.select({currency:r.currency,amount:B(r.amount)}).from(r).innerJoin(e,m(e.id,r.conversionId)).where(d(m(e.campaignId,s),p(r.earnedAt,o),v(r.earnedAt,a),m(r.status,R.confirmed))).groupBy(r.currency)).map(i=>({currency:i.currency,amount:i.amount??"0"}))},["dashboard:campaign-earnings"],{revalidate:S}),_="SELECT uniqMerge(click_count) FROM clicks_daily WHERE date >= {s:Date} AND date < {e:Date} AND campaign_id = {c:UUID}",z=l(async(s,n,t)=>{const{prevStart:o}=y(n,t),a=o.toISOString().slice(0,10),[c,i]=await Promise.all([w(_,{s:n,e:t,c:s}),w(_,{s:a,e:n,c:s})]);return c===null?null:{current:c,previous:i??0}},["dashboard:campaign-click-stats"],{revalidate:S}),G="SELECT toString(date), toString(uniqMerge(click_count)) FROM clicks_daily WHERE date >= {s:Date} AND date < {e:Date} AND campaign_id = {c:UUID} GROUP BY date ORDER BY date",X=l(async(s,n,t)=>{const{start:o,end:a}=y(n,t),c=I(n,t),[i,u]=await Promise.all([L(G,{s:n,e:t,c:s}),f.select({receivedAt:e.receivedAt,event:e.event}).from(e).where(d(m(e.campaignId,s),p(e.receivedAt,o),v(e.receivedAt,a),O(e.event,[A.registration,A.ftd])))]);return x(c,i,u)},["dashboard:campaign-performance-series"],{revalidate:S}),Z=l(async(s,n,t)=>{const{start:o,end:a}=y(n,t),c=I(n,t),i=await f.select({earnedAt:r.earnedAt,amount:r.amount,currency:r.currency}).from(r).innerJoin(e,m(e.id,r.conversionId)).where(d(m(e.campaignId,s),p(r.earnedAt,o),v(r.earnedAt,a),U(r.status,R.reversed)));return T(c,i)},["dashboard:campaign-earnings-series"],{revalidate:S});export{z as getCampaignClickStats,W as getCampaignConversionStats,j as getCampaignEarnings,Z as getCampaignEarningsSeries,X as getCampaignPerformanceSeries};
@@ -0,0 +1,5 @@
1
+ export declare function CampaignCharts({ campaignId, startDate, endDate, }: {
2
+ campaignId: string;
3
+ startDate: string;
4
+ endDate: string;
5
+ }): Promise<import("react").JSX.Element>;
@@ -0,0 +1 @@
1
+ import{jsx as o}from"react/jsx-runtime";import{getCampaignEarningsSeries as t,getCampaignPerformanceSeries as s}from"../campaign-queries.js";import{ChartsGrid as g}from"./charts-grid.js";async function f({campaignId:r,startDate:n,endDate:i}){const[e,a]=await Promise.all([s(r,n,i),t(r,n,i)]);return o(g,{perf:e,money:a,moneyTitleKey:"chartEarningsTitle"})}export{f as CampaignCharts};
@@ -0,0 +1,5 @@
1
+ export declare function CampaignStats({ campaignId, startDate, endDate, }: {
2
+ campaignId: string;
3
+ startDate: string;
4
+ endDate: string;
5
+ }): Promise<import("react").JSX.Element>;
@@ -0,0 +1 @@
1
+ import{jsx as r,jsxs as v}from"react/jsx-runtime";import{getTranslations as g}from"next-intl/server";import{getCampaignClickStats as c,getCampaignConversionStats as p,getCampaignEarnings as d}from"../campaign-queries.js";import{MoneyStatCard as m}from"./money-stat-card.js";import{StatCard as t}from"./stat-card.js";async function f({campaignId:n,startDate:s,endDate:e}){const[a,i,l,o]=await Promise.all([g("Dashboard"),p(n,s,e),d(n,s,e),c(n,s,e)]);return v("div",{className:"grid gap-4 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-5",children:[o&&r(t,{label:a("kpiClicks"),value:o.current,delta:o}),r(t,{label:a("kpiConversions"),value:i.conversions.current,delta:i.conversions}),r(t,{label:a("kpiFtd"),value:i.ftd.current,delta:i.ftd}),r(t,{label:a("kpiRegistrations"),value:i.registrations.current,delta:i.registrations}),r(m,{label:a("kpiEarnings"),entries:l,emptyLabel:a("noData")})]})}export{f as CampaignStats};
@@ -301,6 +301,10 @@
301
301
  "editDialogDesc": "Ändere den Namen des Casinos."
302
302
  },
303
303
  "Campaigns": {
304
+ "DetailTabs": {
305
+ "overview": "Übersicht",
306
+ "performance": "Performance"
307
+ },
304
308
  "Detail": {
305
309
  "backToList": "Zurück zu Kampagnen",
306
310
  "trackingLinkTitle": "Tracking-Link",
@@ -301,6 +301,10 @@
301
301
  "editDialogDesc": "Update the casino's name."
302
302
  },
303
303
  "Campaigns": {
304
+ "DetailTabs": {
305
+ "overview": "Overview",
306
+ "performance": "Performance"
307
+ },
304
308
  "Detail": {
305
309
  "backToList": "Back to campaigns",
306
310
  "trackingLinkTitle": "Tracking link",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "moonhead",
3
- "version": "1.0.4",
3
+ "version": "1.0.5",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "files": [