web: edit report and edit dashboard
This commit is contained in:
@@ -27,7 +27,9 @@ export function NavbarMenu() {
|
|||||||
return (
|
return (
|
||||||
<div className={cn('flex gap-1 items-center text-sm', 'max-sm:flex-col')}>
|
<div className={cn('flex gap-1 items-center text-sm', 'max-sm:flex-col')}>
|
||||||
{params.project && (
|
{params.project && (
|
||||||
<Item href={`/${params.organization}/${params.project}`}>Home</Item>
|
<Item href={`/${params.organization}/${params.project}`}>
|
||||||
|
Dashboards
|
||||||
|
</Item>
|
||||||
)}
|
)}
|
||||||
{params.project && (
|
{params.project && (
|
||||||
<Item href={`/${params.organization}/${params.project}/events`}>
|
<Item href={`/${params.organization}/${params.project}/events`}>
|
||||||
|
|||||||
@@ -47,7 +47,7 @@ export const Chart = memo(
|
|||||||
if (!enabled) {
|
if (!enabled) {
|
||||||
return (
|
return (
|
||||||
<ChartAnimationContainer>
|
<ChartAnimationContainer>
|
||||||
<ChartAnimation name="ballon" className="w-96 mx-auto" />
|
<ChartAnimation name="ballon" className="max-w-sm w-fill mx-auto" />
|
||||||
<p className="text-center font-medium">
|
<p className="text-center font-medium">
|
||||||
Please select at least one event to see the chart.
|
Please select at least one event to see the chart.
|
||||||
</p>
|
</p>
|
||||||
@@ -58,7 +58,7 @@ export const Chart = memo(
|
|||||||
if (chart.isFetching) {
|
if (chart.isFetching) {
|
||||||
return (
|
return (
|
||||||
<ChartAnimationContainer>
|
<ChartAnimationContainer>
|
||||||
<ChartAnimation name="airplane" className="w-96 mx-auto" />
|
<ChartAnimation name="airplane" className="max-w-sm w-fill mx-auto" />
|
||||||
<p className="text-center font-medium">Loading...</p>
|
<p className="text-center font-medium">Loading...</p>
|
||||||
</ChartAnimationContainer>
|
</ChartAnimationContainer>
|
||||||
);
|
);
|
||||||
@@ -67,20 +67,22 @@ export const Chart = memo(
|
|||||||
if (chart.isError) {
|
if (chart.isError) {
|
||||||
return (
|
return (
|
||||||
<ChartAnimationContainer>
|
<ChartAnimationContainer>
|
||||||
<ChartAnimation name="noData" className="w-96 mx-auto" />
|
<ChartAnimation name="noData" className="max-w-sm w-fill mx-auto" />
|
||||||
<p className="text-center font-medium">Something went wrong...</p>
|
<p className="text-center font-medium">Something went wrong...</p>
|
||||||
</ChartAnimationContainer>
|
</ChartAnimationContainer>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!chart.isSuccess) {
|
if (!chart.isSuccess) {
|
||||||
return <ChartAnimation name="ballon" className="w-96 mx-auto" />;
|
return (
|
||||||
|
<ChartAnimation name="ballon" className="max-w-sm w-fill mx-auto" />
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!anyData) {
|
if (!anyData) {
|
||||||
return (
|
return (
|
||||||
<ChartAnimationContainer>
|
<ChartAnimationContainer>
|
||||||
<ChartAnimation name="noData" className="w-96 mx-auto" />
|
<ChartAnimation name="noData" className="max-w-sm w-fill mx-auto" />
|
||||||
<p className="text-center font-medium">No data</p>
|
<p className="text-center font-medium">No data</p>
|
||||||
</ChartAnimationContainer>
|
</ChartAnimationContainer>
|
||||||
);
|
);
|
||||||
@@ -96,7 +98,7 @@ export const Chart = memo(
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<ChartAnimationContainer>
|
<ChartAnimationContainer>
|
||||||
<ChartAnimation name="ballon" className="w-96 mx-auto" />
|
<ChartAnimation name="ballon" className="max-w-sm w-fill mx-auto" />
|
||||||
<p className="text-center font-medium">
|
<p className="text-center font-medium">
|
||||||
Chart type "{chartType}" is not supported yet.
|
Chart type "{chartType}" is not supported yet.
|
||||||
</p>
|
</p>
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ type InitialState = IChartInput & {
|
|||||||
// First approach: define the initial state using that type
|
// First approach: define the initial state using that type
|
||||||
const initialState: InitialState = {
|
const initialState: InitialState = {
|
||||||
dirty: false,
|
dirty: false,
|
||||||
name: 'screen_view',
|
name: 'Untitled',
|
||||||
chartType: 'linear',
|
chartType: 'linear',
|
||||||
interval: 'day',
|
interval: 'day',
|
||||||
breakdowns: [],
|
breakdowns: [],
|
||||||
@@ -30,7 +30,7 @@ const initialState: InitialState = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const reportSlice = createSlice({
|
export const reportSlice = createSlice({
|
||||||
name: 'counter',
|
name: 'report',
|
||||||
initialState,
|
initialState,
|
||||||
reducers: {
|
reducers: {
|
||||||
resetDirty(state) {
|
resetDirty(state) {
|
||||||
@@ -50,6 +50,10 @@ export const reportSlice = createSlice({
|
|||||||
dirty: false,
|
dirty: false,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
setName(state, action: PayloadAction<string>) {
|
||||||
|
state.dirty = true;
|
||||||
|
state.name = action.payload;
|
||||||
|
},
|
||||||
// Events
|
// Events
|
||||||
addEvent: (state, action: PayloadAction<Omit<IChartEvent, 'id'>>) => {
|
addEvent: (state, action: PayloadAction<Omit<IChartEvent, 'id'>>) => {
|
||||||
state.dirty = true;
|
state.dirty = true;
|
||||||
@@ -162,6 +166,7 @@ export const reportSlice = createSlice({
|
|||||||
export const {
|
export const {
|
||||||
reset,
|
reset,
|
||||||
setReport,
|
setReport,
|
||||||
|
setName,
|
||||||
addEvent,
|
addEvent,
|
||||||
removeEvent,
|
removeEvent,
|
||||||
changeEvent,
|
changeEvent,
|
||||||
|
|||||||
75
apps/web/src/modals/EditDashboard.tsx
Normal file
75
apps/web/src/modals/EditDashboard.tsx
Normal file
@@ -0,0 +1,75 @@
|
|||||||
|
import { useEffect } from 'react';
|
||||||
|
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 EditDashboardProps {
|
||||||
|
id: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const validator = z.object({
|
||||||
|
id: z.string().min(1),
|
||||||
|
name: z.string().min(1),
|
||||||
|
});
|
||||||
|
|
||||||
|
type IForm = z.infer<typeof validator>;
|
||||||
|
|
||||||
|
export default function EditDashboard({ id }: EditDashboardProps) {
|
||||||
|
const refetch = useRefetchActive();
|
||||||
|
const mutation = api.dashboard.update.useMutation({
|
||||||
|
onError: handleError,
|
||||||
|
onSuccess() {
|
||||||
|
toast({
|
||||||
|
title: 'Success',
|
||||||
|
description: 'Dashboard updated.',
|
||||||
|
});
|
||||||
|
popModal();
|
||||||
|
refetch();
|
||||||
|
},
|
||||||
|
});
|
||||||
|
const query = api.dashboard.get.useQuery({ id });
|
||||||
|
const data = query.data;
|
||||||
|
const { register, handleSubmit, reset, formState } = useForm<IForm>({
|
||||||
|
resolver: zodResolver(validator),
|
||||||
|
defaultValues: {
|
||||||
|
id: '',
|
||||||
|
name: '',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (data) {
|
||||||
|
reset(data);
|
||||||
|
}
|
||||||
|
}, [data, reset]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ModalContent>
|
||||||
|
<ModalHeader title="Edit dashboard" />
|
||||||
|
<form
|
||||||
|
onSubmit={handleSubmit((values) => {
|
||||||
|
mutation.mutate(values);
|
||||||
|
})}
|
||||||
|
>
|
||||||
|
<InputWithLabel label="Name" placeholder="Name" {...register('name')} />
|
||||||
|
<ButtonContainer>
|
||||||
|
<Button type="button" variant="outline" onClick={() => popModal()}>
|
||||||
|
Cancel
|
||||||
|
</Button>
|
||||||
|
<Button type="submit" disabled={!formState.isDirty}>
|
||||||
|
Update
|
||||||
|
</Button>
|
||||||
|
</ButtonContainer>
|
||||||
|
</form>
|
||||||
|
</ModalContent>
|
||||||
|
);
|
||||||
|
}
|
||||||
49
apps/web/src/modals/EditReport.tsx
Normal file
49
apps/web/src/modals/EditReport.tsx
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
import { useEffect } from 'react';
|
||||||
|
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 type { SubmitHandler } from 'react-hook-form';
|
||||||
|
import { useForm } from 'react-hook-form';
|
||||||
|
import { z } from 'zod';
|
||||||
|
|
||||||
|
import { popModal } from '.';
|
||||||
|
import { ModalContent, ModalHeader } from './Modal/Container';
|
||||||
|
|
||||||
|
const validator = z.object({
|
||||||
|
name: z.string().min(1),
|
||||||
|
});
|
||||||
|
|
||||||
|
type IForm = z.infer<typeof validator>;
|
||||||
|
|
||||||
|
interface EditReportProps {
|
||||||
|
form: IForm;
|
||||||
|
onSubmit: SubmitHandler<IForm>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function EditReport({ form, onSubmit }: EditReportProps) {
|
||||||
|
const { register, handleSubmit, reset, formState } = useForm<IForm>({
|
||||||
|
resolver: zodResolver(validator),
|
||||||
|
defaultValues: form,
|
||||||
|
});
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ModalContent>
|
||||||
|
<ModalHeader title="Edit report" />
|
||||||
|
<form onSubmit={handleSubmit(onSubmit)}>
|
||||||
|
<InputWithLabel label="Name" placeholder="Name" {...register('name')} />
|
||||||
|
<ButtonContainer>
|
||||||
|
<Button type="button" variant="outline" onClick={() => popModal()}>
|
||||||
|
Cancel
|
||||||
|
</Button>
|
||||||
|
<Button type="submit" disabled={!formState.isDirty}>
|
||||||
|
Update
|
||||||
|
</Button>
|
||||||
|
</ButtonContainer>
|
||||||
|
</form>
|
||||||
|
</ModalContent>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -54,7 +54,7 @@ export default function SaveReport({ report }: SaveReportProps) {
|
|||||||
useForm<IForm>({
|
useForm<IForm>({
|
||||||
resolver: zodResolver(validator),
|
resolver: zodResolver(validator),
|
||||||
defaultValues: {
|
defaultValues: {
|
||||||
name: '',
|
name: report.name,
|
||||||
dashboardId: '',
|
dashboardId: '',
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
@@ -136,7 +136,7 @@ export default function SaveReport({ report }: SaveReportProps) {
|
|||||||
<Button type="button" variant="outline" onClick={() => popModal()}>
|
<Button type="button" variant="outline" onClick={() => popModal()}>
|
||||||
Cancel
|
Cancel
|
||||||
</Button>
|
</Button>
|
||||||
<Button type="submit" disabled={!formState.isDirty}>
|
<Button type="submit" disabled={!formState.isValid}>
|
||||||
Save
|
Save
|
||||||
</Button>
|
</Button>
|
||||||
</ButtonContainer>
|
</ButtonContainer>
|
||||||
|
|||||||
@@ -34,6 +34,12 @@ const modals = {
|
|||||||
AddDashboard: dynamic(() => import('./AddDashboard'), {
|
AddDashboard: dynamic(() => import('./AddDashboard'), {
|
||||||
loading: Loading,
|
loading: Loading,
|
||||||
}),
|
}),
|
||||||
|
EditDashboard: dynamic(() => import('./EditDashboard'), {
|
||||||
|
loading: Loading,
|
||||||
|
}),
|
||||||
|
EditReport: dynamic(() => import('./EditReport'), {
|
||||||
|
loading: Loading,
|
||||||
|
}),
|
||||||
};
|
};
|
||||||
|
|
||||||
const emitter = mitt<{
|
const emitter = mitt<{
|
||||||
|
|||||||
@@ -1,17 +1,25 @@
|
|||||||
import { useMemo, useState } from 'react';
|
import { useMemo, useState } from 'react';
|
||||||
|
import { CardActions, CardActionsItem } from '@/components/Card';
|
||||||
import { Container } from '@/components/Container';
|
import { Container } from '@/components/Container';
|
||||||
import { MainLayout } from '@/components/layouts/MainLayout';
|
import { MainLayout } from '@/components/layouts/MainLayout';
|
||||||
import { PageTitle } from '@/components/PageTitle';
|
import { PageTitle } from '@/components/PageTitle';
|
||||||
import { LazyChart } from '@/components/report/chart/LazyChart';
|
import { LazyChart } from '@/components/report/chart/LazyChart';
|
||||||
|
import {
|
||||||
|
DropdownMenu,
|
||||||
|
DropdownMenuContent,
|
||||||
|
DropdownMenuGroup,
|
||||||
|
DropdownMenuItem,
|
||||||
|
DropdownMenuTrigger,
|
||||||
|
} from '@/components/ui/dropdown-menu';
|
||||||
import { RadioGroup, RadioGroupItem } from '@/components/ui/radio-group';
|
import { RadioGroup, RadioGroupItem } from '@/components/ui/radio-group';
|
||||||
import { useOrganizationParams } from '@/hooks/useOrganizationParams';
|
import { useOrganizationParams } from '@/hooks/useOrganizationParams';
|
||||||
import { createServerSideProps } from '@/server/getServerSideProps';
|
import { createServerSideProps } from '@/server/getServerSideProps';
|
||||||
import type { IChartRange } from '@/types';
|
import type { IChartRange } from '@/types';
|
||||||
import { api } from '@/utils/api';
|
import { api, handleError } from '@/utils/api';
|
||||||
import { cn } from '@/utils/cn';
|
import { cn } from '@/utils/cn';
|
||||||
import { timeRanges } from '@/utils/constants';
|
import { timeRanges } from '@/utils/constants';
|
||||||
import { getRangeLabel } from '@/utils/getRangeLabel';
|
import { getRangeLabel } from '@/utils/getRangeLabel';
|
||||||
import { ChevronRight } from 'lucide-react';
|
import { ChevronRight, MoreHorizontal, Trash } from 'lucide-react';
|
||||||
import Link from 'next/link';
|
import Link from 'next/link';
|
||||||
|
|
||||||
export const getServerSideProps = createServerSideProps();
|
export const getServerSideProps = createServerSideProps();
|
||||||
@@ -29,6 +37,13 @@ export default function Dashboard() {
|
|||||||
return query.data?.reports ?? [];
|
return query.data?.reports ?? [];
|
||||||
}, [query]);
|
}, [query]);
|
||||||
|
|
||||||
|
const deletion = api.report.delete.useMutation({
|
||||||
|
onError: handleError,
|
||||||
|
onSuccess() {
|
||||||
|
query.refetch();
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
const [range, setRange] = useState<null | IChartRange>(null);
|
const [range, setRange] = useState<null | IChartRange>(null);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -62,7 +77,7 @@ export default function Dashboard() {
|
|||||||
>
|
>
|
||||||
<Link
|
<Link
|
||||||
href={`/${params.organization}/${params.project}/reports/${report.id}?dashboard=${params.dashboard}`}
|
href={`/${params.organization}/${params.project}/reports/${report.id}?dashboard=${params.dashboard}`}
|
||||||
className="flex border-b border-border p-4 leading-none [&>svg]:hover:opacity-100 items-center justify-between"
|
className="flex border-b border-border p-4 leading-none [&_svg]:hover:opacity-100 items-center justify-between"
|
||||||
shallow
|
shallow
|
||||||
>
|
>
|
||||||
<div>
|
<div>
|
||||||
@@ -76,7 +91,33 @@ export default function Dashboard() {
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
<ChevronRight className="opacity-0 transition-opacity" />
|
<div className="flex items-center gap-4">
|
||||||
|
<DropdownMenu>
|
||||||
|
<DropdownMenuTrigger className="h-8 w-8 hover:border rounded justify-center items-center flex">
|
||||||
|
<MoreHorizontal size={16} />
|
||||||
|
</DropdownMenuTrigger>
|
||||||
|
<DropdownMenuContent align="end" className="w-[200px]">
|
||||||
|
<DropdownMenuGroup>
|
||||||
|
<DropdownMenuItem
|
||||||
|
className="text-destructive"
|
||||||
|
onClick={(event) => {
|
||||||
|
event.stopPropagation();
|
||||||
|
deletion.mutate({
|
||||||
|
reportId: report.id,
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Trash size={16} className="mr-2" />
|
||||||
|
Delete
|
||||||
|
</DropdownMenuItem>
|
||||||
|
</DropdownMenuGroup>
|
||||||
|
</DropdownMenuContent>
|
||||||
|
</DropdownMenu>
|
||||||
|
<ChevronRight
|
||||||
|
className="opacity-10 transition-opacity"
|
||||||
|
size={16}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
</Link>
|
</Link>
|
||||||
<div
|
<div
|
||||||
className={cn(
|
className={cn(
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import { useRefetchActive } from '@/hooks/useRefetchActive';
|
|||||||
import { pushModal } from '@/modals';
|
import { pushModal } from '@/modals';
|
||||||
import { createServerSideProps } from '@/server/getServerSideProps';
|
import { createServerSideProps } from '@/server/getServerSideProps';
|
||||||
import { api, handleError } from '@/utils/api';
|
import { api, handleError } from '@/utils/api';
|
||||||
import { Plus, Trash } from 'lucide-react';
|
import { Pencil, Plus, Trash } from 'lucide-react';
|
||||||
import Link from 'next/link';
|
import Link from 'next/link';
|
||||||
|
|
||||||
export const getServerSideProps = createServerSideProps();
|
export const getServerSideProps = createServerSideProps();
|
||||||
@@ -46,6 +46,18 @@ export default function Home() {
|
|||||||
</Link>
|
</Link>
|
||||||
|
|
||||||
<CardActions>
|
<CardActions>
|
||||||
|
<CardActionsItem className="w-full" asChild>
|
||||||
|
<button
|
||||||
|
onClick={() => {
|
||||||
|
pushModal('EditDashboard', {
|
||||||
|
id: item.id,
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Pencil size={16} />
|
||||||
|
Edit
|
||||||
|
</button>
|
||||||
|
</CardActionsItem>
|
||||||
<CardActionsItem className="text-destructive w-full" asChild>
|
<CardActionsItem className="text-destructive w-full" asChild>
|
||||||
<button
|
<button
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
|
|||||||
@@ -1,20 +1,23 @@
|
|||||||
import { useCallback, useEffect } from 'react';
|
import { useCallback, useEffect } from 'react';
|
||||||
import { Container } from '@/components/Container';
|
import { Container } from '@/components/Container';
|
||||||
import { MainLayout } from '@/components/layouts/MainLayout';
|
import { MainLayout } from '@/components/layouts/MainLayout';
|
||||||
|
import { PageTitle } from '@/components/PageTitle';
|
||||||
import { Chart } from '@/components/report/chart';
|
import { Chart } from '@/components/report/chart';
|
||||||
import { useReportId } from '@/components/report/hooks/useReportId';
|
import { useReportId } from '@/components/report/hooks/useReportId';
|
||||||
import { ReportChartType } from '@/components/report/ReportChartType';
|
import { ReportChartType } from '@/components/report/ReportChartType';
|
||||||
import { ReportDateRange } from '@/components/report/ReportDateRange';
|
import { ReportDateRange } from '@/components/report/ReportDateRange';
|
||||||
import { ReportInterval } from '@/components/report/ReportInterval';
|
import { ReportInterval } from '@/components/report/ReportInterval';
|
||||||
import { ReportSaveButton } from '@/components/report/ReportSaveButton';
|
import { ReportSaveButton } from '@/components/report/ReportSaveButton';
|
||||||
import { reset, setReport } from '@/components/report/reportSlice';
|
import { reset, setName, setReport } from '@/components/report/reportSlice';
|
||||||
import { ReportSidebar } from '@/components/report/sidebar/ReportSidebar';
|
import { ReportSidebar } from '@/components/report/sidebar/ReportSidebar';
|
||||||
import { Button } from '@/components/ui/button';
|
import { Button } from '@/components/ui/button';
|
||||||
import { Sheet, SheetContent, SheetTrigger } from '@/components/ui/sheet';
|
import { Sheet, SheetContent, SheetTrigger } from '@/components/ui/sheet';
|
||||||
import { useRouterBeforeLeave } from '@/hooks/useRouterBeforeLeave';
|
import { useRouterBeforeLeave } from '@/hooks/useRouterBeforeLeave';
|
||||||
|
import { popModal, pushModal } from '@/modals';
|
||||||
import { useDispatch, useSelector } from '@/redux';
|
import { useDispatch, useSelector } from '@/redux';
|
||||||
import { createServerSideProps } from '@/server/getServerSideProps';
|
import { createServerSideProps } from '@/server/getServerSideProps';
|
||||||
import { api } from '@/utils/api';
|
import { api } from '@/utils/api';
|
||||||
|
import { Pencil } from 'lucide-react';
|
||||||
|
|
||||||
export const getServerSideProps = createServerSideProps();
|
export const getServerSideProps = createServerSideProps();
|
||||||
|
|
||||||
@@ -51,6 +54,27 @@ export default function Page() {
|
|||||||
<Sheet>
|
<Sheet>
|
||||||
<MainLayout>
|
<MainLayout>
|
||||||
<Container>
|
<Container>
|
||||||
|
<PageTitle>
|
||||||
|
<span className="flex items-center gap-4">
|
||||||
|
{report.name}
|
||||||
|
<Button
|
||||||
|
variant={'outline'}
|
||||||
|
onClick={() => {
|
||||||
|
pushModal('EditReport', {
|
||||||
|
form: {
|
||||||
|
name: report.name,
|
||||||
|
},
|
||||||
|
onSubmit: (values) => {
|
||||||
|
dispatch(setName(values.name));
|
||||||
|
popModal('EditReport');
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Pencil size={16} />
|
||||||
|
</Button>
|
||||||
|
</span>
|
||||||
|
</PageTitle>
|
||||||
<div className="flex flex-col gap-4 mt-8">
|
<div className="flex flex-col gap-4 mt-8">
|
||||||
<div className="flex flex-col gap-4">
|
<div className="flex flex-col gap-4">
|
||||||
<ReportDateRange />
|
<ReportDateRange />
|
||||||
|
|||||||
@@ -10,12 +10,22 @@ import { z } from 'zod';
|
|||||||
export const dashboardRouter = createTRPCRouter({
|
export const dashboardRouter = createTRPCRouter({
|
||||||
get: protectedProcedure
|
get: protectedProcedure
|
||||||
.input(
|
.input(
|
||||||
z.object({
|
z
|
||||||
slug: z.string(),
|
.object({
|
||||||
})
|
slug: z.string(),
|
||||||
|
})
|
||||||
|
.or(z.object({ id: z.string() }))
|
||||||
)
|
)
|
||||||
.query(async ({ input: { slug } }) => {
|
.query(async ({ input }) => {
|
||||||
return getDashboardBySlug(slug);
|
if ('id' in input) {
|
||||||
|
return db.dashboard.findUnique({
|
||||||
|
where: {
|
||||||
|
id: input.id,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
return getDashboardBySlug(input.slug);
|
||||||
|
}
|
||||||
}),
|
}),
|
||||||
list: protectedProcedure
|
list: protectedProcedure
|
||||||
.input(
|
.input(
|
||||||
@@ -41,6 +51,9 @@ export const dashboardRouter = createTRPCRouter({
|
|||||||
where: {
|
where: {
|
||||||
project_id: projectId,
|
project_id: projectId,
|
||||||
},
|
},
|
||||||
|
orderBy: {
|
||||||
|
createdAt: 'desc',
|
||||||
|
},
|
||||||
});
|
});
|
||||||
}),
|
}),
|
||||||
create: protectedProcedure
|
create: protectedProcedure
|
||||||
@@ -60,6 +73,23 @@ export const dashboardRouter = createTRPCRouter({
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
}),
|
}),
|
||||||
|
update: protectedProcedure
|
||||||
|
.input(
|
||||||
|
z.object({
|
||||||
|
id: z.string(),
|
||||||
|
name: z.string(),
|
||||||
|
})
|
||||||
|
)
|
||||||
|
.mutation(({ input }) => {
|
||||||
|
return db.dashboard.update({
|
||||||
|
where: {
|
||||||
|
id: input.id,
|
||||||
|
},
|
||||||
|
data: {
|
||||||
|
name: input.name,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}),
|
||||||
delete: protectedProcedure
|
delete: protectedProcedure
|
||||||
.input(
|
.input(
|
||||||
z.object({
|
z.object({
|
||||||
|
|||||||
@@ -83,6 +83,9 @@ export const reportRouter = createTRPCRouter({
|
|||||||
project_id: project.id,
|
project_id: project.id,
|
||||||
dashboard_id: dashboard.id,
|
dashboard_id: dashboard.id,
|
||||||
},
|
},
|
||||||
|
orderBy: {
|
||||||
|
createdAt: 'desc',
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
return {
|
return {
|
||||||
@@ -138,4 +141,17 @@ export const reportRouter = createTRPCRouter({
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
}),
|
}),
|
||||||
|
delete: protectedProcedure
|
||||||
|
.input(
|
||||||
|
z.object({
|
||||||
|
reportId: z.string(),
|
||||||
|
})
|
||||||
|
)
|
||||||
|
.mutation(({ input: { reportId } }) => {
|
||||||
|
return db.report.delete({
|
||||||
|
where: {
|
||||||
|
id: reportId,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}),
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user