diff --git a/apps/web/src/components/Card.tsx b/apps/web/src/components/Card.tsx index 1ba4d41d..50432d51 100644 --- a/apps/web/src/components/Card.tsx +++ b/apps/web/src/components/Card.tsx @@ -1,5 +1,20 @@ import type { HtmlProps } from '@/types'; +import { cn } from '@/utils/cn'; -export function Card({ children }: HtmlProps) { - return
{children}
; +type CardProps = HtmlProps & { + hover?: boolean; +}; + +export function Card({ children, hover }: CardProps) { + return ( +
+ {children} +
+ ); } diff --git a/apps/web/src/components/navbar/Breadcrumbs.tsx b/apps/web/src/components/navbar/Breadcrumbs.tsx index a0920c71..88a67fcf 100644 --- a/apps/web/src/components/navbar/Breadcrumbs.tsx +++ b/apps/web/src/components/navbar/Breadcrumbs.tsx @@ -29,6 +29,16 @@ export function Breadcrumbs() { } ); + const dashboard = api.dashboard.get.useQuery( + { + slug: params.dashboard, + }, + { + enabled: !!params.dashboard, + staleTime: Infinity, + } + ); + return (
@@ -52,6 +62,18 @@ export function Breadcrumbs() { )} + + {org.data && pro.data && dashboard.data && ( + <> + + + {dashboard.data.name} + + + )}
); diff --git a/apps/web/src/components/navbar/NavbarMenu.tsx b/apps/web/src/components/navbar/NavbarMenu.tsx index 8447d74b..53f8e3e3 100644 --- a/apps/web/src/components/navbar/NavbarMenu.tsx +++ b/apps/web/src/components/navbar/NavbarMenu.tsx @@ -1,5 +1,6 @@ import { useOrganizationParams } from '@/hooks/useOrganizationParams'; import { cn } from '@/utils/cn'; +import { strip } from '@/utils/object'; import Link from 'next/link'; import { NavbarUserDropdown } from './NavbarUserDropdown'; @@ -29,7 +30,12 @@ export function NavbarMenu() { {params.project && ( Create report diff --git a/apps/web/src/components/report/ReportSaveButton.tsx b/apps/web/src/components/report/ReportSaveButton.tsx index 2465d3f6..03cdc067 100644 --- a/apps/web/src/components/report/ReportSaveButton.tsx +++ b/apps/web/src/components/report/ReportSaveButton.tsx @@ -1,16 +1,19 @@ import { Button } from '@/components/ui/button'; import { toast } from '@/components/ui/use-toast'; import { pushModal } from '@/modals'; -import { useSelector } from '@/redux'; +import { useDispatch, useSelector } from '@/redux'; import { api, handleError } from '@/utils/api'; import { SaveIcon } from 'lucide-react'; import { useReportId } from './hooks/useReportId'; +import { resetDirty } from './reportSlice'; export function ReportSaveButton() { const { reportId } = useReportId(); + const dispatch = useDispatch(); const update = api.report.update.useMutation({ onSuccess() { + dispatch(resetDirty()); toast({ title: 'Success', description: 'Report updated.', diff --git a/apps/web/src/components/report/chart/ChartAnimation.tsx b/apps/web/src/components/report/chart/ChartAnimation.tsx index 9c377f3a..7d30102f 100644 --- a/apps/web/src/components/report/chart/ChartAnimation.tsx +++ b/apps/web/src/components/report/chart/ChartAnimation.tsx @@ -1,11 +1,14 @@ import airplane from '@/lottie/airplane.json'; import ballon from '@/lottie/ballon.json'; +import noData from '@/lottie/no-data.json'; +import { cn } from '@/utils/cn'; import type { LottieComponentProps } from 'lottie-react'; import Lottie from 'lottie-react'; const animations = { airplane, ballon, + noData, }; type Animations = keyof typeof animations; @@ -15,3 +18,12 @@ export const ChartAnimation = ({ }: Omit & { name: Animations; }) => ; + +export const ChartAnimationContainer = ( + props: React.ButtonHTMLAttributes +) => ( +
+); diff --git a/apps/web/src/components/report/chart/index.tsx b/apps/web/src/components/report/chart/index.tsx index 5b003965..107c0d62 100644 --- a/apps/web/src/components/report/chart/index.tsx +++ b/apps/web/src/components/report/chart/index.tsx @@ -3,7 +3,7 @@ import { useOrganizationParams } from '@/hooks/useOrganizationParams'; import type { IChartInput } from '@/types'; import { api } from '@/utils/api'; -import { ChartAnimation } from './ChartAnimation'; +import { ChartAnimation, ChartAnimationContainer } from './ChartAnimation'; import { withChartProivder } from './ChartProvider'; import { ReportBarChart } from './ReportBarChart'; import { ReportLineChart } from './ReportLineChart'; @@ -45,15 +45,32 @@ export const Chart = memo( const anyData = Boolean(chart.data?.series?.[0]?.data); if (!enabled) { - return

Select events & filters to begin

; + return ( + + +

+ Please select at least one event to see the chart. +

+
+ ); } if (chart.isFetching) { - return ; + return ( + + +

Loading...

+
+ ); } if (chart.isError) { - return

Error

; + return ( + + +

Something went wrong...

+
+ ); } if (!chart.isSuccess) { @@ -61,7 +78,12 @@ export const Chart = memo( } if (!anyData) { - return ; + return ( + + +

No data

+
+ ); } if (chartType === 'bar') { @@ -72,6 +94,13 @@ export const Chart = memo( return ; } - return

Chart type "{chartType}" is not supported yet.

; + return ( + + +

+ Chart type "{chartType}" is not supported yet. +

+
+ ); }) ); diff --git a/apps/web/src/components/report/reportSlice.ts b/apps/web/src/components/report/reportSlice.ts index 4c1e0152..5f66fc65 100644 --- a/apps/web/src/components/report/reportSlice.ts +++ b/apps/web/src/components/report/reportSlice.ts @@ -33,6 +33,12 @@ export const reportSlice = createSlice({ name: 'counter', initialState, reducers: { + resetDirty(state) { + return { + ...state, + dirty: false, + }; + }, reset() { return initialState; }, @@ -165,6 +171,7 @@ export const { changeInterval, changeDateRanges, changeChartType, + resetDirty, } = reportSlice.actions; export default reportSlice.reducer; diff --git a/apps/web/src/lottie/no-data.json b/apps/web/src/lottie/no-data.json new file mode 100644 index 00000000..1d8c0de0 --- /dev/null +++ b/apps/web/src/lottie/no-data.json @@ -0,0 +1 @@ +{"v":"5.5.9","fr":60,"ip":0,"op":180,"w":426,"h":290,"nm":"search_empty","ddd":0,"assets":[{"id":"comp_0","layers":[{"ddd":0,"ind":1,"ty":4,"nm":"mouth","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":44,"s":[211,145,0],"to":[0,0.667,0],"ti":[0,-0.667,0]},{"t":69,"s":[211,149,0]}],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"t":44,"s":[100,100,100]},{"t":68,"s":[105,105,100]}],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0.5,0.25],[-1.715,0.372]],"o":[[-13.025,-6.512],[7.5,-1.625]],"v":[[-10,12.25],[-31.75,12.625]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.40784313725490196,0.4627450980392157,0.5176470588235295,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":3600,"st":0,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":"汗","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":51,"s":[0]},{"t":95,"s":[100]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":51,"s":[248,95.252,0],"to":[0,1.5,0],"ti":[0,-1.5,0]},{"t":94,"s":[248,104.252,0]}],"ix":2},"a":{"a":0,"k":[0,-19.333,0],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":51,"s":[40,40,100]},{"t":95,"s":[100,100,100]}],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-9.94,0],[0,7.27],[0,0],[0,-7.27]],"o":[[9.94,0],[0,-7.27],[0,0],[0,7.27]],"v":[[0,19],[18,5.84],[0,-19],[-18,5.84]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"gf","o":{"a":0,"k":100,"ix":10},"r":1,"bm":0,"g":{"p":3,"k":{"a":0,"k":[0,0.69,0.976,1,0.5,0.714,0.954,0.997,1,0.738,0.932,0.994],"ix":9}},"s":{"a":0,"k":[0.019,-9.236],"ix":5},"e":{"a":0,"k":[10.208,19],"ix":6},"t":1,"nm":"Gradient Fill 1","mn":"ADBE Vector Graphic - G-Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"汗","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":569,"st":0,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":"eye","parent":1,"sr":1,"ks":{"o":{"a":0,"k":92,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[-7.064,-11.347,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"t":0,"s":[100,100,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"t":3,"s":[100,20,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"t":6,"s":[100,100,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"t":9,"s":[100,20,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"t":12,"s":[100,100,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"t":50,"s":[100,100,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"t":53,"s":[100,20,100]},{"t":56,"s":[100,100,100]}],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[8.466,16.135],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.23529411764705882,0.26666666666666666,0.29411764705882354,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"eye","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":569,"st":0,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":"eye","parent":1,"sr":1,"ks":{"o":{"a":0,"k":92,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[-33.767,-11.347,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"t":0,"s":[100,100,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"t":3,"s":[100,20,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"t":6,"s":[100,100,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"t":9,"s":[100,20,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"t":12,"s":[100,100,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"t":50,"s":[100,100,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"t":53,"s":[100,20,100]},{"t":56,"s":[100,100,100]}],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[8.466,16.135],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.23529411764705882,0.26666666666666666,0.29411764705882354,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"eye","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":569,"st":0,"bm":0},{"ddd":0,"ind":5,"ty":0,"nm":"magnifier","refId":"comp_1","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[213,145,0],"ix":2},"a":{"a":0,"k":[213,145,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"w":426,"h":290,"ip":0,"op":569,"st":0,"bm":0}]},{"id":"comp_1","layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Shape Layer 1","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[213,145,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"st","c":{"a":0,"k":[0.40784313725490196,0.4627450980392157,0.5176470588235295,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":3600,"st":0,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":"橢圓形","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":-5,"ix":10},"p":{"a":0,"k":[190.106,142.222,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[99.994,100.091],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":-40,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"橢圓形","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":3600,"st":0,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":"橢圓形","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":-5,"ix":10},"p":{"a":0,"k":[192.013,142.223,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-26.81,22.62],[22.49,26.96],[26.8,-22.62],[-22.5,-26.95]],"o":[[26.81,-22.62],[-22.49,-26.96],[-26.81,22.62],[22.49,26.96]],"v":[[40.727,48.812],[48.537,-40.958],[-40.723,-48.808],[-48.533,40.952]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.3764705882352941,0.596078431372549,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"橢圓形","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":3600,"st":0,"bm":0},{"ddd":0,"ind":5,"ty":4,"nm":"矩形","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":-5,"ix":10},"p":{"a":0,"k":[268.024,226.941,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[42.094,74.507],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":10,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.2980392156862745,0.49019607843137253,0.8392156862745098,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":-40,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"矩形","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":3600,"st":0,"bm":0},{"ddd":0,"ind":6,"ty":4,"nm":"矩形","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":-5,"ix":10},"p":{"a":0,"k":[238.933,197.25,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[24.446,44.702],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.847000002861,0.847000002861,0.847000002861,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":-40,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"矩形","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":3600,"st":0,"bm":0}]},{"id":"comp_2","layers":[{"ddd":0,"ind":1,"ty":4,"nm":"合併形狀","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":60,"s":[0]},{"t":265,"s":[360]}],"ix":10},"p":{"a":0,"k":[208,27,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[16,2],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"rc","d":1,"s":{"a":0,"k":[2,18],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rectangle Path 2","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"mm","mm":2,"nm":"Merge Paths 1","mn":"ADBE Vector Filter - Merge","hd":false},{"ty":"fl","c":{"a":0,"k":[0.796078026295,0.835294008255,0.843137025833,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"合併形狀","np":4,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":3600,"st":0,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":"合併形狀","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":5,"s":[0]},{"t":358,"s":[360]}],"ix":10},"p":{"a":0,"k":[418,107,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[16,2],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"rc","d":1,"s":{"a":0,"k":[2,18],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rectangle Path 2","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"mm","mm":2,"nm":"Merge Paths 1","mn":"ADBE Vector Filter - Merge","hd":false},{"ty":"fl","c":{"a":0,"k":[0.796078026295,0.835294008255,0.843137025833,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"合併形狀","np":4,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":3600,"st":0,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":"合併形狀","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":5,"s":[0]},{"t":281,"s":[360]}],"ix":10},"p":{"a":0,"k":[31,196,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[16,2],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"rc","d":1,"s":{"a":0,"k":[2,18],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rectangle Path 2","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"mm","mm":2,"nm":"Merge Paths 1","mn":"ADBE Vector Filter - Merge","hd":false},{"ty":"fl","c":{"a":0,"k":[0.796078026295,0.835294008255,0.843137025833,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"合併形狀","np":4,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":3600,"st":0,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":"橢圓形","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":0,"s":[0]},{"t":281,"s":[360]}],"ix":10},"p":{"a":0,"k":[70,33,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[24,24],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"st","c":{"a":0,"k":[0.701960980892,0.756862998009,0.776471018791,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"橢圓形","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":3600,"st":0,"bm":0}]},{"id":"comp_3","layers":[{"ddd":0,"ind":1,"ty":4,"nm":"橢圓形","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[306,9,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[16,16],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"st","c":{"a":0,"k":[0.701960980892,0.756862998009,0.776471018791,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"橢圓形","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":3600,"st":0,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":"橢圓形","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[376,179,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[16,16],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.701960980892,0.768626987934,0.776471018791,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"橢圓形","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":3600,"st":0,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":"橢圓形","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[8,115,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[16,16],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.701960980892,0.768626987934,0.776471018791,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"橢圓形","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":3600,"st":0,"bm":0}]}],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"shadow","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[191,145,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"t":0,"s":[70,100,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"t":30,"s":[50,100,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"t":60,"s":[70,100,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"t":90,"s":[50,100,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"t":120,"s":[70,100,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"t":150,"s":[50,100,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"t":180,"s":[70,100,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"t":210,"s":[50,100,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"t":240,"s":[70,100,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"t":270,"s":[50,100,100]},{"t":295,"s":[70,100,100]}],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[56.697,6.631],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.848757874732,0.861812935623,0.876608455882,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-9.651,124.815],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":300,"st":0,"bm":0},{"ddd":0,"ind":2,"ty":0,"nm":"main","refId":"comp_0","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":0,"s":[213,145,0],"to":[0,0.833,0],"ti":[0,0,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":30,"s":[213,150,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":60,"s":[213,145,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":90,"s":[213,150,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":120,"s":[213,145,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":150,"s":[213,150,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":180,"s":[213,145,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":210,"s":[213,150,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":240,"s":[213,145,0],"to":[0,0,0],"ti":[0,-0.833,0]},{"t":270,"s":[213,150,0]}],"ix":2},"a":{"a":0,"k":[213,145,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"w":426,"h":290,"ip":0,"op":3600,"st":0,"bm":0},{"ddd":0,"ind":3,"ty":0,"nm":"decoration1","refId":"comp_2","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.981],"y":[1.128]},"o":{"x":[0.39],"y":[0.019]},"t":8,"s":[0]},{"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":40,"s":[100]},{"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":72,"s":[80]},{"t":73,"s":[100]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[213,289,0],"ix":2},"a":{"a":0,"k":[213,289,0],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0.833,0.833,0.667],"y":[0.866,0.866,1]},"o":{"x":[0.37,0.37,0.333],"y":[-0.029,-0.029,0]},"t":0,"s":[0,0,100]},{"i":{"x":[0.833,0.833,0.833],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":40,"s":[100,100,100]},{"i":{"x":[0.833,0.833,0.833],"y":[1,1,1]},"o":{"x":[0.167,0.167,0.167],"y":[0,0,0]},"t":72,"s":[80,80,100]},{"t":133,"s":[90,90,100]}],"ix":6}},"ao":0,"w":426,"h":290,"ip":0,"op":569,"st":0,"bm":0},{"ddd":0,"ind":4,"ty":0,"nm":"decoratio2","refId":"comp_3","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":0,"s":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":41,"s":[100]},{"t":72,"s":[100]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[211,291,0],"ix":2},"a":{"a":0,"k":[211,291,0],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"t":0,"s":[0,0,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"t":43,"s":[99.442,99.442,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"t":72,"s":[84.442,84.442,100]},{"t":133,"s":[90,90,100]}],"ix":6}},"ao":0,"w":426,"h":290,"ip":0,"op":569,"st":0,"bm":0}],"markers":[{"tm":180,"cm":"1","dr":0},{"tm":3600,"cm":"2","dr":0}]} \ No newline at end of file diff --git a/apps/web/src/modals/AddDashboard.tsx b/apps/web/src/modals/AddDashboard.tsx new file mode 100644 index 00000000..f2cd7b2e --- /dev/null +++ b/apps/web/src/modals/AddDashboard.tsx @@ -0,0 +1,78 @@ +import { ButtonContainer } from '@/components/ButtonContainer'; +import { InputWithLabel } from '@/components/forms/InputWithLabel'; +import { Button } from '@/components/ui/button'; +import { toast } from '@/components/ui/use-toast'; +import { useRefetchActive } from '@/hooks/useRefetchActive'; +import { api, handleError } from '@/utils/api'; +import { zodResolver } from '@hookform/resolvers/zod'; +import { useForm } from 'react-hook-form'; +import { z } from 'zod'; + +import { popModal } from '.'; +import { ModalContent, ModalHeader } from './Modal/Container'; + +interface AddDashboardProps { + organizationSlug: string; + projectSlug: string; +} + +const validator = z.object({ + name: z.string().min(1, 'Required'), +}); + +type IForm = z.infer; + +export default function AddDashboard({ + // organizationSlug, + projectSlug, +}: AddDashboardProps) { + const refetch = useRefetchActive(); + + const { register, handleSubmit, formState } = useForm({ + resolver: zodResolver(validator), + defaultValues: { + name: '', + }, + }); + + const mutation = api.dashboard.create.useMutation({ + onError: handleError, + onSuccess() { + refetch(); + toast({ + title: 'Success', + description: 'Dashboard created.', + }); + popModal(); + }, + }); + + return ( + + +
{ + mutation.mutate({ + name, + projectSlug, + }); + })} + > + + + + + + +
+ ); +} diff --git a/apps/web/src/modals/SaveReport.tsx b/apps/web/src/modals/SaveReport.tsx index 768ec979..985c15ff 100644 --- a/apps/web/src/modals/SaveReport.tsx +++ b/apps/web/src/modals/SaveReport.tsx @@ -1,3 +1,4 @@ +import { useEffect } from 'react'; import { ButtonContainer } from '@/components/ButtonContainer'; import { InputWithLabel } from '@/components/forms/InputWithLabel'; import { Button } from '@/components/ui/button'; @@ -8,6 +9,7 @@ import { useOrganizationParams } from '@/hooks/useOrganizationParams'; import { useRefetchActive } from '@/hooks/useRefetchActive'; import type { IChartInput } from '@/types'; import { api, handleError } from '@/utils/api'; +import { strip } from '@/utils/object'; import { zodResolver } from '@hookform/resolvers/zod'; import { useRouter } from 'next/router'; import { Controller, useForm } from 'react-hook-form'; @@ -30,7 +32,7 @@ type IForm = z.infer; export default function SaveReport({ report }: SaveReportProps) { const router = useRouter(); - const { organization, project } = useOrganizationParams(); + const { organization, project, dashboard } = useOrganizationParams(); const refetch = useRefetchActive(); const save = api.report.save.useMutation({ onError: handleError, @@ -41,7 +43,10 @@ export default function SaveReport({ report }: SaveReportProps) { }); popModal(); refetch(); - router.push(`/${organization}/${project}/reports/${res.id}`); + router.push({ + pathname: `/${organization}/${project}/reports/${res.id}`, + query: strip({ dashboard }), + }); }, }); @@ -58,7 +63,7 @@ export default function SaveReport({ report }: SaveReportProps) { onError: handleError, onSuccess(res) { setValue('dashboardId', res.id); - dashboasrdQuery.refetch(); + dashboardQuery.refetch(); toast({ title: 'Success', description: 'Dashboard created.', @@ -66,15 +71,24 @@ export default function SaveReport({ report }: SaveReportProps) { }, }); - const dashboasrdQuery = api.dashboard.list.useQuery({ + const dashboardQuery = api.dashboard.list.useQuery({ projectSlug: project, }); - const dashboards = (dashboasrdQuery.data ?? []).map((item) => ({ + const dashboards = (dashboardQuery.data ?? []).map((item) => ({ value: item.id, label: item.name, })); + useEffect(() => { + if (dashboard && dashboardQuery.data) { + const match = dashboardQuery.data.find((item) => item.slug === dashboard); + if (match) { + setValue('dashboardId', match.id); + } + } + }, [dashboard, dashboardQuery]); + return ( diff --git a/apps/web/src/modals/index.tsx b/apps/web/src/modals/index.tsx index 02254c4d..68ba7cd1 100644 --- a/apps/web/src/modals/index.tsx +++ b/apps/web/src/modals/index.tsx @@ -31,6 +31,9 @@ const modals = { SaveReport: dynamic(() => import('./SaveReport'), { loading: Loading, }), + AddDashboard: dynamic(() => import('./AddDashboard'), { + loading: Loading, + }), }; const emitter = mitt<{ diff --git a/apps/web/src/pages/[organization]/[project]/[dashboard].tsx b/apps/web/src/pages/[organization]/[project]/[dashboard].tsx index ed1c0e5b..60e79b3b 100644 --- a/apps/web/src/pages/[organization]/[project]/[dashboard].tsx +++ b/apps/web/src/pages/[organization]/[project]/[dashboard].tsx @@ -11,6 +11,7 @@ import { api } from '@/utils/api'; import { cn } from '@/utils/cn'; import { timeRanges } from '@/utils/constants'; import { getRangeLabel } from '@/utils/getRangeLabel'; +import { ChevronRight } from 'lucide-react'; import Link from 'next/link'; export const getServerSideProps = createServerSideProps(); @@ -60,19 +61,22 @@ export default function Dashboard() { key={report.id} > -
{report.name}
- {chartRange !== null && ( -
- - {chartRange} - - {range !== null && {getRangeLabel(range)}} -
- )} +
+
{report.name}
+ {chartRange !== null && ( +
+ + {chartRange} + + {range !== null && {getRangeLabel(range)}} +
+ )} +
+
Dashboards
{dashboards.map((item) => ( - + {item.name} ))} + + +
diff --git a/apps/web/src/pages/[organization]/index.tsx b/apps/web/src/pages/[organization]/index.tsx index af3f5c46..b015cc4c 100644 --- a/apps/web/src/pages/[organization]/index.tsx +++ b/apps/web/src/pages/[organization]/index.tsx @@ -29,10 +29,10 @@ export default function Home() { Projects
{projects.map((item) => ( - + {item.name} diff --git a/apps/web/src/server/api/routers/chart.ts b/apps/web/src/server/api/routers/chart.ts index 24689adb..d73cc0c2 100644 --- a/apps/web/src/server/api/routers/chart.ts +++ b/apps/web/src/server/api/routers/chart.ts @@ -455,8 +455,6 @@ async function getChartData({ ); } - console.log(sql); - // group by sql label const series = result.reduce( (acc, item) => { @@ -528,8 +526,8 @@ function fillEmptySpotsInTimeline( } if (interval === 'month') { - clonedStartDate.setDate(1); - clonedEndDate.setDate(1); + clonedStartDate.setUTCDate(1); + clonedEndDate.setUTCDate(1); } // Force if interval is month and the start date is the same month as today @@ -537,11 +535,17 @@ function fillEmptySpotsInTimeline( interval === 'month' && clonedStartDate.getUTCFullYear() === today.getUTCFullYear() && clonedStartDate.getUTCMonth() === today.getUTCMonth(); - + let prev = undefined; while ( shouldForce() || clonedStartDate.getTime() <= clonedEndDate.getTime() ) { + if (prev === clonedStartDate.getTime()) { + console.log('GET OUT NOW!'); + break; + } + prev = clonedStartDate.getTime(); + const getYear = (date: Date) => date.getUTCFullYear(); const getMonth = (date: Date) => date.getUTCMonth(); const getDay = (date: Date) => date.getUTCDate(); @@ -598,19 +602,19 @@ function fillEmptySpotsInTimeline( switch (interval) { case 'day': { - clonedStartDate.setDate(clonedStartDate.getUTCDate() + 1); + clonedStartDate.setUTCDate(clonedStartDate.getUTCDate() + 1); break; } case 'hour': { - clonedStartDate.setHours(clonedStartDate.getUTCHours() + 1); + clonedStartDate.setUTCHours(clonedStartDate.getUTCHours() + 1); break; } case 'minute': { - clonedStartDate.setMinutes(clonedStartDate.getUTCMinutes() + 1); + clonedStartDate.setUTCMinutes(clonedStartDate.getUTCMinutes() + 1); break; } case 'month': { - clonedStartDate.setMonth(clonedStartDate.getUTCMonth() + 1); + clonedStartDate.setUTCMonth(clonedStartDate.getUTCMonth() + 1); break; } } diff --git a/apps/web/src/server/api/routers/dashboard.ts b/apps/web/src/server/api/routers/dashboard.ts index bfc6633a..aefa6cbe 100644 --- a/apps/web/src/server/api/routers/dashboard.ts +++ b/apps/web/src/server/api/routers/dashboard.ts @@ -1,10 +1,20 @@ import { createTRPCRouter, protectedProcedure } from '@/server/api/trpc'; import { db } from '@/server/db'; +import { getDashboardBySlug } from '@/server/services/dashboard.service'; import { getProjectBySlug } from '@/server/services/project.service'; import { slug } from '@/utils/slug'; import { z } from 'zod'; export const dashboardRouter = createTRPCRouter({ + get: protectedProcedure + .input( + z.object({ + slug: z.string(), + }) + ) + .query(async ({ input: { slug } }) => { + return getDashboardBySlug(slug); + }), list: protectedProcedure .input( z diff --git a/apps/web/src/types/index.ts b/apps/web/src/types/index.ts index 08fa92fd..c001fabf 100644 --- a/apps/web/src/types/index.ts +++ b/apps/web/src/types/index.ts @@ -12,7 +12,10 @@ import type { Client, Project } from '@prisma/client'; import type { TooltipProps } from 'recharts'; import type { z } from 'zod'; -export type HtmlProps = React.DetailedHTMLProps, T>; +export type HtmlProps = Omit< + React.DetailedHTMLProps, T>, + 'ref' +>; export type IChartInput = z.infer; export type IChartInputWithDates = z.infer; diff --git a/apps/web/src/utils/object.ts b/apps/web/src/utils/object.ts index 6d8fb566..7f23ec22 100644 --- a/apps/web/src/utils/object.ts +++ b/apps/web/src/utils/object.ts @@ -1,3 +1,5 @@ +import { anyPass, isEmpty, isNil, reject } from 'ramda'; + export function toDots( obj: Record, path = '' @@ -16,3 +18,5 @@ export function toDots( }; }, {}); } + +export const strip = reject(anyPass([isEmpty, isNil]));