Merge pull request #2 from Openpanel-dev/feature/rename-db-columns
Feature/rename db columns
This commit is contained in:
@@ -33,10 +33,10 @@ const startServer = async () => {
|
|||||||
fastify.register(profileRouter, { prefix: '/profile' });
|
fastify.register(profileRouter, { prefix: '/profile' });
|
||||||
fastify.register(liveRouter, { prefix: '/live' });
|
fastify.register(liveRouter, { prefix: '/live' });
|
||||||
fastify.register(miscRouter, { prefix: '/misc' });
|
fastify.register(miscRouter, { prefix: '/misc' });
|
||||||
fastify.setErrorHandler((error, request, reply) => {
|
fastify.setErrorHandler((error) => {
|
||||||
fastify.log.error(error);
|
fastify.log.error(error);
|
||||||
});
|
});
|
||||||
fastify.get('/', (request, reply) => {
|
fastify.get('/', (_request, reply) => {
|
||||||
reply.send({ name: 'openpanel sdk api' });
|
reply.send({ name: 'openpanel sdk api' });
|
||||||
});
|
});
|
||||||
// fastify.get('/health-check', async (request, reply) => {
|
// fastify.get('/health-check', async (request, reply) => {
|
||||||
|
|||||||
@@ -89,5 +89,5 @@ export async function validateSdkRequest(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return client.project_id;
|
return client.projectId;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -71,7 +71,7 @@
|
|||||||
"next-themes": "^0.2.1",
|
"next-themes": "^0.2.1",
|
||||||
"nuqs": "^1.16.1",
|
"nuqs": "^1.16.1",
|
||||||
"prisma-error-enum": "^0.1.3",
|
"prisma-error-enum": "^0.1.3",
|
||||||
"pushmodal": "^0.0.8",
|
"pushmodal": "^1.0.0",
|
||||||
"ramda": "^0.29.1",
|
"ramda": "^0.29.1",
|
||||||
"random-animal-name": "^0.1.1",
|
"random-animal-name": "^0.1.1",
|
||||||
"react": "18.2.0",
|
"react": "18.2.0",
|
||||||
|
|||||||
@@ -1,27 +0,0 @@
|
|||||||
import PageLayout from '@/app/(app)/[organizationId]/[projectId]/page-layout';
|
|
||||||
|
|
||||||
import {
|
|
||||||
getClientsByOrganizationId,
|
|
||||||
getProjectsByOrganizationSlug,
|
|
||||||
} from '@openpanel/db';
|
|
||||||
|
|
||||||
import ListProjects from './list-projects';
|
|
||||||
|
|
||||||
interface PageProps {
|
|
||||||
params: {
|
|
||||||
organizationId: string;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export default async function Page({ params: { organizationId } }: PageProps) {
|
|
||||||
const [projects, clients] = await Promise.all([
|
|
||||||
getProjectsByOrganizationSlug(organizationId),
|
|
||||||
getClientsByOrganizationId(organizationId),
|
|
||||||
]);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<PageLayout title="Projects" organizationSlug={organizationId}>
|
|
||||||
<ListProjects projects={projects} clients={clients} />
|
|
||||||
</PageLayout>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
import { StickyBelowHeader } from '@/app/(app)/[organizationId]/[projectId]/layout-sticky-below-header';
|
import { StickyBelowHeader } from '@/app/(app)/[organizationSlug]/[projectId]/layout-sticky-below-header';
|
||||||
import { useOverviewOptions } from '@/components/overview/useOverviewOptions';
|
import { useOverviewOptions } from '@/components/overview/useOverviewOptions';
|
||||||
import { LazyChart } from '@/components/report/chart/LazyChart';
|
import { LazyChart } from '@/components/report/chart/LazyChart';
|
||||||
import { Button } from '@/components/ui/button';
|
import { Button } from '@/components/ui/button';
|
||||||
@@ -12,10 +12,12 @@ import {
|
|||||||
DropdownMenuTrigger,
|
DropdownMenuTrigger,
|
||||||
} from '@/components/ui/dropdown-menu';
|
} from '@/components/ui/dropdown-menu';
|
||||||
import { useAppParams } from '@/hooks/useAppParams';
|
import { useAppParams } from '@/hooks/useAppParams';
|
||||||
|
import { api, handleError } from '@/trpc/client';
|
||||||
import { cn } from '@/utils/cn';
|
import { cn } from '@/utils/cn';
|
||||||
import { ChevronRight, MoreHorizontal, PlusIcon, Trash } from 'lucide-react';
|
import { ChevronRight, MoreHorizontal, PlusIcon, Trash } from 'lucide-react';
|
||||||
import Link from 'next/link';
|
import Link from 'next/link';
|
||||||
import { useRouter } from 'next/navigation';
|
import { useRouter } from 'next/navigation';
|
||||||
|
import { toast } from 'sonner';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
getDefaultIntervalByDates,
|
getDefaultIntervalByDates,
|
||||||
@@ -33,7 +35,13 @@ export function ListReports({ reports }: ListReportsProps) {
|
|||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const params = useAppParams<{ dashboardId: string }>();
|
const params = useAppParams<{ dashboardId: string }>();
|
||||||
const { range, startDate, endDate } = useOverviewOptions();
|
const { range, startDate, endDate } = useOverviewOptions();
|
||||||
|
const deletion = api.report.delete.useMutation({
|
||||||
|
onError: handleError,
|
||||||
|
onSuccess() {
|
||||||
|
router.refresh();
|
||||||
|
toast('Report deleted');
|
||||||
|
},
|
||||||
|
});
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<StickyBelowHeader className="flex items-center justify-between p-4">
|
<StickyBelowHeader className="flex items-center justify-between p-4">
|
||||||
@@ -42,7 +50,7 @@ export function ListReports({ reports }: ListReportsProps) {
|
|||||||
icon={PlusIcon}
|
icon={PlusIcon}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
router.push(
|
router.push(
|
||||||
`/${params.organizationId}/${
|
`/${params.organizationSlug}/${
|
||||||
params.projectId
|
params.projectId
|
||||||
}/reports?${new URLSearchParams({
|
}/reports?${new URLSearchParams({
|
||||||
dashboardId: params.dashboardId,
|
dashboardId: params.dashboardId,
|
||||||
@@ -60,7 +68,7 @@ export function ListReports({ reports }: ListReportsProps) {
|
|||||||
return (
|
return (
|
||||||
<div className="card" key={report.id}>
|
<div className="card" key={report.id}>
|
||||||
<Link
|
<Link
|
||||||
href={`/${params.organizationId}/${params.projectId}/reports/${report.id}`}
|
href={`/${params.organizationSlug}/${params.projectId}/reports/${report.id}`}
|
||||||
className="flex items-center justify-between border-b border-border p-4 leading-none [&_svg]:hover:opacity-100"
|
className="flex items-center justify-between border-b border-border p-4 leading-none [&_svg]:hover:opacity-100"
|
||||||
shallow
|
shallow
|
||||||
>
|
>
|
||||||
@@ -95,10 +103,10 @@ export function ListReports({ reports }: ListReportsProps) {
|
|||||||
<DropdownMenuItem
|
<DropdownMenuItem
|
||||||
className="text-destructive"
|
className="text-destructive"
|
||||||
onClick={(event) => {
|
onClick={(event) => {
|
||||||
// event.stopPropagation();
|
event.stopPropagation();
|
||||||
// deletion.mutate({
|
deletion.mutate({
|
||||||
// reportId: report.id,
|
reportId: report.id,
|
||||||
// });
|
});
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Trash size={16} className="mr-2" />
|
<Trash size={16} className="mr-2" />
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
import PageLayout from '@/app/(app)/[organizationId]/[projectId]/page-layout';
|
import PageLayout from '@/app/(app)/[organizationSlug]/[projectId]/page-layout';
|
||||||
import { notFound } from 'next/navigation';
|
import { notFound } from 'next/navigation';
|
||||||
|
|
||||||
import { getDashboardById, getReportsByDashboardId } from '@openpanel/db';
|
import { getDashboardById, getReportsByDashboardId } from '@openpanel/db';
|
||||||
@@ -7,14 +7,14 @@ import { ListReports } from './list-reports';
|
|||||||
|
|
||||||
interface PageProps {
|
interface PageProps {
|
||||||
params: {
|
params: {
|
||||||
organizationId: string;
|
organizationSlug: string;
|
||||||
projectId: string;
|
projectId: string;
|
||||||
dashboardId: string;
|
dashboardId: string;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export default async function Page({
|
export default async function Page({
|
||||||
params: { organizationId, projectId, dashboardId },
|
params: { organizationSlug, projectId, dashboardId },
|
||||||
}: PageProps) {
|
}: PageProps) {
|
||||||
const [dashboard, reports] = await Promise.all([
|
const [dashboard, reports] = await Promise.all([
|
||||||
getDashboardById(dashboardId, projectId),
|
getDashboardById(dashboardId, projectId),
|
||||||
@@ -26,7 +26,7 @@ export default async function Page({
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<PageLayout title={dashboard.name} organizationSlug={organizationId}>
|
<PageLayout title={dashboard.name} organizationSlug={organizationSlug}>
|
||||||
<ListReports reports={reports} />
|
<ListReports reports={reports} />
|
||||||
</PageLayout>
|
</PageLayout>
|
||||||
);
|
);
|
||||||
@@ -20,7 +20,7 @@ interface ListDashboardsProps {
|
|||||||
export function ListDashboards({ dashboards }: ListDashboardsProps) {
|
export function ListDashboards({ dashboards }: ListDashboardsProps) {
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const params = useAppParams();
|
const params = useAppParams();
|
||||||
const { organizationId, projectId } = params;
|
const { organizationSlug, projectId } = params;
|
||||||
const deletion = api.dashboard.delete.useMutation({
|
const deletion = api.dashboard.delete.useMutation({
|
||||||
onError: (error, variables) => {
|
onError: (error, variables) => {
|
||||||
return handleErrorToastOptions({
|
return handleErrorToastOptions({
|
||||||
@@ -65,8 +65,8 @@ export function ListDashboards({ dashboards }: ListDashboardsProps) {
|
|||||||
<Card key={item.id} hover>
|
<Card key={item.id} hover>
|
||||||
<div>
|
<div>
|
||||||
<Link
|
<Link
|
||||||
href={`/${organizationId}/${projectId}/dashboards/${item.id}`}
|
href={`/${organizationSlug}/${projectId}/dashboards/${item.id}`}
|
||||||
className="block flex flex-col p-4"
|
className="flex flex-col p-4"
|
||||||
>
|
>
|
||||||
<span className="font-medium">{item.name}</span>
|
<span className="font-medium">{item.name}</span>
|
||||||
</Link>
|
</Link>
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
import PageLayout from '@/app/(app)/[organizationId]/[projectId]/page-layout';
|
import PageLayout from '@/app/(app)/[organizationSlug]/[projectId]/page-layout';
|
||||||
|
|
||||||
import { getDashboardsByProjectId } from '@openpanel/db';
|
import { getDashboardsByProjectId } from '@openpanel/db';
|
||||||
|
|
||||||
@@ -8,17 +8,17 @@ import { ListDashboards } from './list-dashboards';
|
|||||||
interface PageProps {
|
interface PageProps {
|
||||||
params: {
|
params: {
|
||||||
projectId: string;
|
projectId: string;
|
||||||
organizationId: string;
|
organizationSlug: string;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export default async function Page({
|
export default async function Page({
|
||||||
params: { projectId, organizationId },
|
params: { projectId, organizationSlug },
|
||||||
}: PageProps) {
|
}: PageProps) {
|
||||||
const dashboards = await getDashboardsByProjectId(projectId);
|
const dashboards = await getDashboardsByProjectId(projectId);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<PageLayout title="Dashboards" organizationSlug={organizationId}>
|
<PageLayout title="Dashboards" organizationSlug={organizationSlug}>
|
||||||
{dashboards.length > 0 && <HeaderDashboards />}
|
{dashboards.length > 0 && <HeaderDashboards />}
|
||||||
<ListDashboards dashboards={dashboards} />
|
<ListDashboards dashboards={dashboards} />
|
||||||
</PageLayout>
|
</PageLayout>
|
||||||
@@ -1,4 +1,3 @@
|
|||||||
import { Widget } from '@/components/widget';
|
|
||||||
import { escape } from 'sqlstring';
|
import { escape } from 'sqlstring';
|
||||||
|
|
||||||
import { db, getEvents } from '@openpanel/db';
|
import { db, getEvents } from '@openpanel/db';
|
||||||
@@ -12,7 +11,7 @@ interface Props {
|
|||||||
export default async function EventConversionsListServer({ projectId }: Props) {
|
export default async function EventConversionsListServer({ projectId }: Props) {
|
||||||
const conversions = await db.eventMeta.findMany({
|
const conversions = await db.eventMeta.findMany({
|
||||||
where: {
|
where: {
|
||||||
project_id: projectId,
|
projectId,
|
||||||
conversion: true,
|
conversion: true,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
@@ -15,7 +15,7 @@ import { EventIcon } from './event-icon';
|
|||||||
type EventListItemProps = IServiceCreateEventPayload;
|
type EventListItemProps = IServiceCreateEventPayload;
|
||||||
|
|
||||||
export function EventListItem(props: EventListItemProps) {
|
export function EventListItem(props: EventListItemProps) {
|
||||||
const { organizationId, projectId } = useAppParams();
|
const { organizationSlug, projectId } = useAppParams();
|
||||||
const { createdAt, name, path, duration, meta, profile } = props;
|
const { createdAt, name, path, duration, meta, profile } = props;
|
||||||
const [isDetailsOpen, setIsDetailsOpen] = useState(false);
|
const [isDetailsOpen, setIsDetailsOpen] = useState(false);
|
||||||
|
|
||||||
@@ -77,7 +77,7 @@ export function EventListItem(props: EventListItemProps) {
|
|||||||
onClick={(e) => {
|
onClick={(e) => {
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
}}
|
}}
|
||||||
href={`/${organizationId}/${projectId}/profiles/${profile?.id}`}
|
href={`/${organizationSlug}/${projectId}/profiles/${profile?.id}`}
|
||||||
className="max-w-[80px] overflow-hidden text-ellipsis whitespace-nowrap text-sm text-muted-foreground hover:underline"
|
className="max-w-[80px] overflow-hidden text-ellipsis whitespace-nowrap text-sm text-muted-foreground hover:underline"
|
||||||
>
|
>
|
||||||
{profile?.firstName} {profile?.lastName}
|
{profile?.firstName} {profile?.lastName}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
import PageLayout from '@/app/(app)/[organizationId]/[projectId]/page-layout';
|
import PageLayout from '@/app/(app)/[organizationSlug]/[projectId]/page-layout';
|
||||||
import { OverviewFiltersButtons } from '@/components/overview/filters/overview-filters-buttons';
|
import { OverviewFiltersButtons } from '@/components/overview/filters/overview-filters-buttons';
|
||||||
import { OverviewFiltersDrawer } from '@/components/overview/filters/overview-filters-drawer';
|
import { OverviewFiltersDrawer } from '@/components/overview/filters/overview-filters-drawer';
|
||||||
import {
|
import {
|
||||||
@@ -17,7 +17,7 @@ import { EventList } from './event-list';
|
|||||||
interface PageProps {
|
interface PageProps {
|
||||||
params: {
|
params: {
|
||||||
projectId: string;
|
projectId: string;
|
||||||
organizationId: string;
|
organizationSlug: string;
|
||||||
};
|
};
|
||||||
searchParams: {
|
searchParams: {
|
||||||
events?: string;
|
events?: string;
|
||||||
@@ -31,7 +31,7 @@ const nuqsOptions = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export default async function Page({
|
export default async function Page({
|
||||||
params: { projectId, organizationId },
|
params: { projectId, organizationSlug },
|
||||||
searchParams,
|
searchParams,
|
||||||
}: PageProps) {
|
}: PageProps) {
|
||||||
const filters =
|
const filters =
|
||||||
@@ -56,7 +56,7 @@ export default async function Page({
|
|||||||
]);
|
]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<PageLayout title="Events" organizationSlug={organizationId}>
|
<PageLayout title="Events" organizationSlug={organizationSlug}>
|
||||||
<StickyBelowHeader className="flex justify-between p-4">
|
<StickyBelowHeader className="flex justify-between p-4">
|
||||||
<OverviewFiltersDrawer
|
<OverviewFiltersDrawer
|
||||||
mode="events"
|
mode="events"
|
||||||
@@ -83,49 +83,49 @@ export default function LayoutMenu({ dashboards }: LayoutMenuProps) {
|
|||||||
<LinkWithIcon
|
<LinkWithIcon
|
||||||
icon={WallpaperIcon}
|
icon={WallpaperIcon}
|
||||||
label="Overview"
|
label="Overview"
|
||||||
href={`/${params.organizationId}/${projectId}`}
|
href={`/${params.organizationSlug}/${projectId}`}
|
||||||
/>
|
/>
|
||||||
<LinkWithIcon
|
<LinkWithIcon
|
||||||
icon={LayoutPanelTopIcon}
|
icon={LayoutPanelTopIcon}
|
||||||
label="Dashboards"
|
label="Dashboards"
|
||||||
href={`/${params.organizationId}/${projectId}/dashboards`}
|
href={`/${params.organizationSlug}/${projectId}/dashboards`}
|
||||||
/>
|
/>
|
||||||
<LinkWithIcon
|
<LinkWithIcon
|
||||||
icon={GanttChartIcon}
|
icon={GanttChartIcon}
|
||||||
label="Events"
|
label="Events"
|
||||||
href={`/${params.organizationId}/${projectId}/events`}
|
href={`/${params.organizationSlug}/${projectId}/events`}
|
||||||
/>
|
/>
|
||||||
<LinkWithIcon
|
<LinkWithIcon
|
||||||
icon={UsersIcon}
|
icon={UsersIcon}
|
||||||
label="Profiles"
|
label="Profiles"
|
||||||
href={`/${params.organizationId}/${projectId}/profiles`}
|
href={`/${params.organizationSlug}/${projectId}/profiles`}
|
||||||
/>
|
/>
|
||||||
<LinkWithIcon
|
<LinkWithIcon
|
||||||
icon={CogIcon}
|
icon={CogIcon}
|
||||||
label="Settings"
|
label="Settings"
|
||||||
href={`/${params.organizationId}/${projectId}/settings/organization`}
|
href={`/${params.organizationSlug}/${projectId}/settings/organization`}
|
||||||
/>
|
/>
|
||||||
{pathname?.includes('/settings/') && (
|
{pathname?.includes('/settings/') && (
|
||||||
<div className="flex flex-col gap-1 pl-7">
|
<div className="flex flex-col gap-1 pl-7">
|
||||||
<LinkWithIcon
|
<LinkWithIcon
|
||||||
icon={BuildingIcon}
|
icon={BuildingIcon}
|
||||||
label="Organization"
|
label="Organization"
|
||||||
href={`/${params.organizationId}/${projectId}/settings/organization`}
|
href={`/${params.organizationSlug}/${projectId}/settings/organization`}
|
||||||
/>
|
/>
|
||||||
<LinkWithIcon
|
<LinkWithIcon
|
||||||
icon={WarehouseIcon}
|
icon={WarehouseIcon}
|
||||||
label="Projects"
|
label="Projects"
|
||||||
href={`/${params.organizationId}/${projectId}/settings/projects`}
|
href={`/${params.organizationSlug}/${projectId}/settings/projects`}
|
||||||
/>
|
/>
|
||||||
<LinkWithIcon
|
<LinkWithIcon
|
||||||
icon={UserIcon}
|
icon={UserIcon}
|
||||||
label="Profile (yours)"
|
label="Profile (yours)"
|
||||||
href={`/${params.organizationId}/${projectId}/settings/profile`}
|
href={`/${params.organizationSlug}/${projectId}/settings/profile`}
|
||||||
/>
|
/>
|
||||||
<LinkWithIcon
|
<LinkWithIcon
|
||||||
icon={BookmarkIcon}
|
icon={BookmarkIcon}
|
||||||
label="References"
|
label="References"
|
||||||
href={`/${params.organizationId}/${projectId}/settings/references`}
|
href={`/${params.organizationSlug}/${projectId}/settings/references`}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
@@ -145,7 +145,7 @@ export default function LayoutMenu({ dashboards }: LayoutMenuProps) {
|
|||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
href={`/${item.organization_slug}/${item.project_id}/dashboards/${item.id}`}
|
href={`/${item.organizationSlug}/${item.projectId}/dashboards/${item.id}`}
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
@@ -18,7 +18,7 @@ export default function LayoutOrganizationSelector({
|
|||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
|
|
||||||
const organization = organizations.find(
|
const organization = organizations.find(
|
||||||
(item) => item.slug === params.organizationId
|
(item) => item.slug === params.organizationSlug
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!organization) {
|
if (!organization) {
|
||||||
@@ -13,7 +13,7 @@ export default function LayoutProjectSelector({
|
|||||||
projects,
|
projects,
|
||||||
}: LayoutProjectSelectorProps) {
|
}: LayoutProjectSelectorProps) {
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const { organizationId, projectId } = useAppParams();
|
const { organizationSlug, projectId } = useAppParams();
|
||||||
const pathname = usePathname() || '';
|
const pathname = usePathname() || '';
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -24,12 +24,12 @@ export default function LayoutProjectSelector({
|
|||||||
className="w-auto min-w-0 max-sm:max-w-[100px]"
|
className="w-auto min-w-0 max-sm:max-w-[100px]"
|
||||||
placeholder={'Select project'}
|
placeholder={'Select project'}
|
||||||
onChange={(value) => {
|
onChange={(value) => {
|
||||||
if (organizationId && projectId) {
|
if (organizationSlug && projectId) {
|
||||||
const split = pathname.replace(projectId, value).split('/');
|
const split = pathname.replace(projectId, value).split('/');
|
||||||
// slicing here will remove everything after /{orgId}/{projectId}/dashboards [slice here] /xxx/xxx/xxx
|
// slicing here will remove everything after /{orgId}/{projectId}/dashboards [slice here] /xxx/xxx/xxx
|
||||||
router.push(split.slice(0, 4).join('/'));
|
router.push(split.slice(0, 4).join('/'));
|
||||||
} else {
|
} else {
|
||||||
router.push(`/${organizationId}/${value}`);
|
router.push(`/${organizationSlug}/${value}`);
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
value={projectId}
|
value={projectId}
|
||||||
@@ -17,13 +17,13 @@ import LayoutOrganizationSelector from './layout-organization-selector';
|
|||||||
interface LayoutSidebarProps {
|
interface LayoutSidebarProps {
|
||||||
organizations: IServiceOrganization[];
|
organizations: IServiceOrganization[];
|
||||||
dashboards: IServiceDashboards;
|
dashboards: IServiceDashboards;
|
||||||
organizationId: string;
|
organizationSlug: string;
|
||||||
projectId: string;
|
projectId: string;
|
||||||
}
|
}
|
||||||
export function LayoutSidebar({
|
export function LayoutSidebar({
|
||||||
organizations,
|
organizations,
|
||||||
dashboards,
|
dashboards,
|
||||||
organizationId,
|
organizationSlug,
|
||||||
projectId,
|
projectId,
|
||||||
}: LayoutSidebarProps) {
|
}: LayoutSidebarProps) {
|
||||||
const [active, setActive] = useState(false);
|
const [active, setActive] = useState(false);
|
||||||
@@ -69,7 +69,7 @@ export function LayoutSidebar({
|
|||||||
<div className="flex flex-col gap-2 bg-background p-4 pt-0">
|
<div className="flex flex-col gap-2 bg-background p-4 pt-0">
|
||||||
<Link
|
<Link
|
||||||
className={cn('flex gap-2', buttonVariants())}
|
className={cn('flex gap-2', buttonVariants())}
|
||||||
href={`/${organizationId}/${projectId}/reports`}
|
href={`/${organizationSlug}/${projectId}/reports`}
|
||||||
>
|
>
|
||||||
<PlusIcon size={16} />
|
<PlusIcon size={16} />
|
||||||
Create a report
|
Create a report
|
||||||
@@ -11,22 +11,22 @@ import { LayoutSidebar } from './layout-sidebar';
|
|||||||
interface AppLayoutProps {
|
interface AppLayoutProps {
|
||||||
children: React.ReactNode;
|
children: React.ReactNode;
|
||||||
params: {
|
params: {
|
||||||
organizationId: string;
|
organizationSlug: string;
|
||||||
projectId: string;
|
projectId: string;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export default async function AppLayout({
|
export default async function AppLayout({
|
||||||
children,
|
children,
|
||||||
params: { organizationId, projectId },
|
params: { organizationSlug, projectId },
|
||||||
}: AppLayoutProps) {
|
}: AppLayoutProps) {
|
||||||
const [organizations, projects, dashboards] = await Promise.all([
|
const [organizations, projects, dashboards] = await Promise.all([
|
||||||
getCurrentOrganizations(),
|
getCurrentOrganizations(),
|
||||||
getCurrentProjects(organizationId),
|
getCurrentProjects(organizationSlug),
|
||||||
getDashboardsByProjectId(projectId),
|
getDashboardsByProjectId(projectId),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
if (!organizations.find((item) => item.slug === organizationId)) {
|
if (!organizations.find((item) => item.slug === organizationSlug)) {
|
||||||
return (
|
return (
|
||||||
<FullPageEmptyState
|
<FullPageEmptyState
|
||||||
title="Could not find organization"
|
title="Could not find organization"
|
||||||
@@ -51,7 +51,7 @@ export default async function AppLayout({
|
|||||||
return (
|
return (
|
||||||
<div id="dashboard">
|
<div id="dashboard">
|
||||||
<LayoutSidebar
|
<LayoutSidebar
|
||||||
{...{ organizationId, projectId, organizations, dashboards }}
|
{...{ organizationSlug, projectId, organizations, dashboards }}
|
||||||
/>
|
/>
|
||||||
<div className="transition-all lg:pl-72">{children}</div>
|
<div className="transition-all lg:pl-72">{children}</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
import PageLayout from '@/app/(app)/[organizationId]/[projectId]/page-layout';
|
import PageLayout from '@/app/(app)/[organizationSlug]/[projectId]/page-layout';
|
||||||
import { OverviewFiltersButtons } from '@/components/overview/filters/overview-filters-buttons';
|
import { OverviewFiltersButtons } from '@/components/overview/filters/overview-filters-buttons';
|
||||||
import { OverviewFiltersDrawer } from '@/components/overview/filters/overview-filters-drawer';
|
import { OverviewFiltersDrawer } from '@/components/overview/filters/overview-filters-drawer';
|
||||||
import ServerLiveCounter from '@/components/overview/live-counter';
|
import ServerLiveCounter from '@/components/overview/live-counter';
|
||||||
@@ -10,7 +10,7 @@ import OverviewTopGeo from '@/components/overview/overview-top-geo';
|
|||||||
import OverviewTopPages from '@/components/overview/overview-top-pages';
|
import OverviewTopPages from '@/components/overview/overview-top-pages';
|
||||||
import OverviewTopSources from '@/components/overview/overview-top-sources';
|
import OverviewTopSources from '@/components/overview/overview-top-sources';
|
||||||
|
|
||||||
import { db } from '@openpanel/db';
|
import { getShareByProjectId } from '@openpanel/db';
|
||||||
|
|
||||||
import OverviewMetrics from '../../../../components/overview/overview-metrics';
|
import OverviewMetrics from '../../../../components/overview/overview-metrics';
|
||||||
import { StickyBelowHeader } from './layout-sticky-below-header';
|
import { StickyBelowHeader } from './layout-sticky-below-header';
|
||||||
@@ -18,22 +18,18 @@ import { OverviewReportRange } from './overview-sticky-header';
|
|||||||
|
|
||||||
interface PageProps {
|
interface PageProps {
|
||||||
params: {
|
params: {
|
||||||
organizationId: string;
|
organizationSlug: string;
|
||||||
projectId: string;
|
projectId: string;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export default async function Page({
|
export default async function Page({
|
||||||
params: { organizationId, projectId },
|
params: { organizationSlug, projectId },
|
||||||
}: PageProps) {
|
}: PageProps) {
|
||||||
const share = await db.shareOverview.findUnique({
|
const share = await getShareByProjectId(projectId);
|
||||||
where: {
|
|
||||||
project_id: projectId,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<PageLayout title="Overview" organizationSlug={organizationId}>
|
<PageLayout title="Overview" organizationSlug={organizationSlug}>
|
||||||
<StickyBelowHeader>
|
<StickyBelowHeader>
|
||||||
<div className="flex justify-between gap-2 p-4">
|
<div className="flex justify-between gap-2 p-4">
|
||||||
<div className="flex gap-2">
|
<div className="flex gap-2">
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
import PageLayout from '@/app/(app)/[organizationId]/[projectId]/page-layout';
|
import PageLayout from '@/app/(app)/[organizationSlug]/[projectId]/page-layout';
|
||||||
import { OverviewFiltersButtons } from '@/components/overview/filters/overview-filters-buttons';
|
import { OverviewFiltersButtons } from '@/components/overview/filters/overview-filters-buttons';
|
||||||
import { OverviewFiltersDrawer } from '@/components/overview/filters/overview-filters-drawer';
|
import { OverviewFiltersDrawer } from '@/components/overview/filters/overview-filters-drawer';
|
||||||
import { ProfileAvatar } from '@/components/profiles/profile-avatar';
|
import { ProfileAvatar } from '@/components/profiles/profile-avatar';
|
||||||
@@ -27,9 +27,9 @@ import { StickyBelowHeader } from '../../layout-sticky-below-header';
|
|||||||
|
|
||||||
interface PageProps {
|
interface PageProps {
|
||||||
params: {
|
params: {
|
||||||
|
organizationSlug: string;
|
||||||
projectId: string;
|
projectId: string;
|
||||||
profileId: string;
|
profileId: string;
|
||||||
organizationId: string;
|
|
||||||
};
|
};
|
||||||
searchParams: {
|
searchParams: {
|
||||||
events?: string;
|
events?: string;
|
||||||
@@ -41,7 +41,7 @@ interface PageProps {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export default async function Page({
|
export default async function Page({
|
||||||
params: { projectId, profileId, organizationId },
|
params: { projectId, profileId, organizationSlug },
|
||||||
searchParams,
|
searchParams,
|
||||||
}: PageProps) {
|
}: PageProps) {
|
||||||
const eventListOptions: GetEventListOptions = {
|
const eventListOptions: GetEventListOptions = {
|
||||||
@@ -125,7 +125,7 @@ export default async function Page({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<PageLayout
|
<PageLayout
|
||||||
organizationSlug={organizationId}
|
organizationSlug={organizationSlug}
|
||||||
title={
|
title={
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
<ProfileAvatar {...profile} size="sm" className="hidden sm:block" />
|
<ProfileAvatar {...profile} size="sm" className="hidden sm:block" />
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
import PageLayout from '@/app/(app)/[organizationId]/[projectId]/page-layout';
|
import PageLayout from '@/app/(app)/[organizationSlug]/[projectId]/page-layout';
|
||||||
import { OverviewFiltersButtons } from '@/components/overview/filters/overview-filters-buttons';
|
import { OverviewFiltersButtons } from '@/components/overview/filters/overview-filters-buttons';
|
||||||
import { OverviewFiltersDrawer } from '@/components/overview/filters/overview-filters-drawer';
|
import { OverviewFiltersDrawer } from '@/components/overview/filters/overview-filters-drawer';
|
||||||
import { eventQueryFiltersParser } from '@/hooks/useEventQueryFilters';
|
import { eventQueryFiltersParser } from '@/hooks/useEventQueryFilters';
|
||||||
@@ -11,7 +11,7 @@ import ProfileTopServer from './profile-top';
|
|||||||
|
|
||||||
interface PageProps {
|
interface PageProps {
|
||||||
params: {
|
params: {
|
||||||
organizationId: string;
|
organizationSlug: string;
|
||||||
projectId: string;
|
projectId: string;
|
||||||
};
|
};
|
||||||
searchParams: {
|
searchParams: {
|
||||||
@@ -25,11 +25,11 @@ const nuqsOptions = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export default function Page({
|
export default function Page({
|
||||||
params: { organizationId, projectId },
|
params: { organizationSlug, projectId },
|
||||||
searchParams: { cursor, f },
|
searchParams: { cursor, f },
|
||||||
}: PageProps) {
|
}: PageProps) {
|
||||||
return (
|
return (
|
||||||
<PageLayout title="Profiles" organizationSlug={organizationId}>
|
<PageLayout title="Profiles" organizationSlug={organizationSlug}>
|
||||||
{/* <StickyBelowHeader className="flex justify-between p-4">
|
{/* <StickyBelowHeader className="flex justify-between p-4">
|
||||||
<OverviewFiltersDrawer
|
<OverviewFiltersDrawer
|
||||||
projectId={projectId}
|
projectId={projectId}
|
||||||
@@ -53,7 +53,7 @@ export default function Page({
|
|||||||
<ProfileLastSeenServer projectId={projectId} />
|
<ProfileLastSeenServer projectId={projectId} />
|
||||||
<ProfileTopServer
|
<ProfileTopServer
|
||||||
projectId={projectId}
|
projectId={projectId}
|
||||||
organizationId={organizationId}
|
organizationSlug={organizationSlug}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -21,7 +21,7 @@ interface ProfileListProps {
|
|||||||
count: number;
|
count: number;
|
||||||
}
|
}
|
||||||
export function ProfileList({ data, count }: ProfileListProps) {
|
export function ProfileList({ data, count }: ProfileListProps) {
|
||||||
const { organizationId, projectId } = useAppParams();
|
const { organizationSlug, projectId } = useAppParams();
|
||||||
const { cursor, setCursor } = useCursor();
|
const { cursor, setCursor } = useCursor();
|
||||||
return (
|
return (
|
||||||
<Widget>
|
<Widget>
|
||||||
@@ -46,7 +46,7 @@ export function ProfileList({ data, count }: ProfileListProps) {
|
|||||||
render(profile) {
|
render(profile) {
|
||||||
return (
|
return (
|
||||||
<Link
|
<Link
|
||||||
href={`/${organizationId}/${projectId}/profiles/${profile.id}`}
|
href={`/${organizationSlug}/${projectId}/profiles/${profile.id}`}
|
||||||
className="flex items-center gap-2 font-medium"
|
className="flex items-center gap-2 font-medium"
|
||||||
>
|
>
|
||||||
<ProfileAvatar size="sm" {...profile} />
|
<ProfileAvatar size="sm" {...profile} />
|
||||||
@@ -10,11 +10,11 @@ import { chQuery, getProfiles } from '@openpanel/db';
|
|||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
projectId: string;
|
projectId: string;
|
||||||
organizationId: string;
|
organizationSlug: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default async function ProfileTopServer({
|
export default async function ProfileTopServer({
|
||||||
organizationId,
|
organizationSlug,
|
||||||
projectId,
|
projectId,
|
||||||
}: Props) {
|
}: Props) {
|
||||||
// Days since last event from users
|
// Days since last event from users
|
||||||
@@ -44,7 +44,7 @@ export default async function ProfileTopServer({
|
|||||||
render(profile) {
|
render(profile) {
|
||||||
return (
|
return (
|
||||||
<Link
|
<Link
|
||||||
href={`/${organizationId}/${projectId}/profiles/${profile.id}`}
|
href={`/${organizationSlug}/${projectId}/profiles/${profile.id}`}
|
||||||
className="flex items-center gap-2 font-medium"
|
className="flex items-center gap-2 font-medium"
|
||||||
>
|
>
|
||||||
<ProfileAvatar size="sm" {...profile} />
|
<ProfileAvatar size="sm" {...profile} />
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
import PageLayout from '@/app/(app)/[organizationId]/[projectId]/page-layout';
|
import PageLayout from '@/app/(app)/[organizationSlug]/[projectId]/page-layout';
|
||||||
import { Pencil } from 'lucide-react';
|
import { Pencil } from 'lucide-react';
|
||||||
import { notFound } from 'next/navigation';
|
import { notFound } from 'next/navigation';
|
||||||
|
|
||||||
@@ -8,14 +8,14 @@ import ReportEditor from '../report-editor';
|
|||||||
|
|
||||||
interface PageProps {
|
interface PageProps {
|
||||||
params: {
|
params: {
|
||||||
|
organizationSlug: string;
|
||||||
projectId: string;
|
projectId: string;
|
||||||
reportId: string;
|
reportId: string;
|
||||||
organizationId: string;
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export default async function Page({
|
export default async function Page({
|
||||||
params: { reportId, organizationId },
|
params: { reportId, organizationSlug },
|
||||||
}: PageProps) {
|
}: PageProps) {
|
||||||
const report = await getReportById(reportId);
|
const report = await getReportById(reportId);
|
||||||
|
|
||||||
@@ -25,7 +25,7 @@ export default async function Page({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<PageLayout
|
<PageLayout
|
||||||
organizationSlug={organizationId}
|
organizationSlug={organizationSlug}
|
||||||
title={
|
title={
|
||||||
<div className="flex cursor-pointer items-center gap-2">
|
<div className="flex cursor-pointer items-center gap-2">
|
||||||
{report.name}
|
{report.name}
|
||||||
@@ -1,19 +1,19 @@
|
|||||||
import PageLayout from '@/app/(app)/[organizationId]/[projectId]/page-layout';
|
import PageLayout from '@/app/(app)/[organizationSlug]/[projectId]/page-layout';
|
||||||
import { Pencil } from 'lucide-react';
|
import { Pencil } from 'lucide-react';
|
||||||
|
|
||||||
import ReportEditor from './report-editor';
|
import ReportEditor from './report-editor';
|
||||||
|
|
||||||
interface PageProps {
|
interface PageProps {
|
||||||
params: {
|
params: {
|
||||||
organizationId: string;
|
organizationSlug: string;
|
||||||
projectId: string;
|
projectId: string;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function Page({ params: { organizationId } }: PageProps) {
|
export default function Page({ params: { organizationSlug } }: PageProps) {
|
||||||
return (
|
return (
|
||||||
<PageLayout
|
<PageLayout
|
||||||
organizationSlug={organizationId}
|
organizationSlug={organizationSlug}
|
||||||
title={
|
title={
|
||||||
<div className="flex cursor-pointer items-center gap-2">
|
<div className="flex cursor-pointer items-center gap-2">
|
||||||
Unnamed report
|
Unnamed report
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
import { useEffect } from 'react';
|
import { useEffect } from 'react';
|
||||||
import { StickyBelowHeader } from '@/app/(app)/[organizationId]/[projectId]/layout-sticky-below-header';
|
import { StickyBelowHeader } from '@/app/(app)/[organizationSlug]/[projectId]/layout-sticky-below-header';
|
||||||
import { ChartSwitch } from '@/components/report/chart';
|
import { ChartSwitch } from '@/components/report/chart';
|
||||||
import { ReportChartType } from '@/components/report/ReportChartType';
|
import { ReportChartType } from '@/components/report/ReportChartType';
|
||||||
import { ReportInterval } from '@/components/report/ReportInterval';
|
import { ReportInterval } from '@/components/report/ReportInterval';
|
||||||
@@ -33,7 +33,7 @@ interface Props {
|
|||||||
|
|
||||||
export default function CreateInvite({ projects }: Props) {
|
export default function CreateInvite({ projects }: Props) {
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const { organizationId: organizationSlug } = useAppParams();
|
const { organizationSlug } = useAppParams();
|
||||||
|
|
||||||
const { register, handleSubmit, formState, reset, control } = useForm<IForm>({
|
const { register, handleSubmit, formState, reset, control } = useForm<IForm>({
|
||||||
resolver: zodResolver(zInviteUser),
|
resolver: zodResolver(zInviteUser),
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
import PageLayout from '@/app/(app)/[organizationId]/[projectId]/page-layout';
|
import PageLayout from '@/app/(app)/[organizationSlug]/[projectId]/page-layout';
|
||||||
import { notFound } from 'next/navigation';
|
import { notFound } from 'next/navigation';
|
||||||
|
|
||||||
import { getInvites, getOrganizationBySlug } from '@openpanel/db';
|
import { getOrganizationBySlug } from '@openpanel/db';
|
||||||
|
|
||||||
import EditOrganization from './edit-organization';
|
import EditOrganization from './edit-organization';
|
||||||
import InvitesServer from './invites';
|
import InvitesServer from './invites';
|
||||||
@@ -9,12 +9,12 @@ import MembersServer from './members';
|
|||||||
|
|
||||||
interface PageProps {
|
interface PageProps {
|
||||||
params: {
|
params: {
|
||||||
organizationId: string;
|
organizationSlug: string;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export default async function Page({
|
export default async function Page({
|
||||||
params: { organizationId: organizationSlug },
|
params: { organizationSlug },
|
||||||
}: PageProps) {
|
}: PageProps) {
|
||||||
const organization = await getOrganizationBySlug(organizationSlug);
|
const organization = await getOrganizationBySlug(organizationSlug);
|
||||||
|
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
import PageLayout from '@/app/(app)/[organizationId]/[projectId]/page-layout';
|
import PageLayout from '@/app/(app)/[organizationSlug]/[projectId]/page-layout';
|
||||||
import { auth } from '@clerk/nextjs';
|
import { auth } from '@clerk/nextjs';
|
||||||
|
|
||||||
import { getUserById } from '@openpanel/db';
|
import { getUserById } from '@openpanel/db';
|
||||||
@@ -8,15 +8,17 @@ import { Logout } from './logout';
|
|||||||
|
|
||||||
interface PageProps {
|
interface PageProps {
|
||||||
params: {
|
params: {
|
||||||
organizationId: string;
|
organizationSlug: string;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
export default async function Page({ params: { organizationId } }: PageProps) {
|
export default async function Page({
|
||||||
|
params: { organizationSlug },
|
||||||
|
}: PageProps) {
|
||||||
const { userId } = auth();
|
const { userId } = auth();
|
||||||
const profile = await getUserById(userId!);
|
const profile = await getUserById(userId!);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<PageLayout title={profile.lastName} organizationSlug={organizationId}>
|
<PageLayout title={profile.lastName} organizationSlug={organizationSlug}>
|
||||||
<div className="flex flex-col gap-4 p-4">
|
<div className="flex flex-col gap-4 p-4">
|
||||||
<EditProfile profile={profile} />
|
<EditProfile profile={profile} />
|
||||||
<Logout />
|
<Logout />
|
||||||
@@ -1,9 +1,8 @@
|
|||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
import { StickyBelowHeader } from '@/app/(app)/[organizationId]/[projectId]/layout-sticky-below-header';
|
import { StickyBelowHeader } from '@/app/(app)/[organizationSlug]/[projectId]/layout-sticky-below-header';
|
||||||
import { ClientActions } from '@/components/clients/client-actions';
|
import { ClientActions } from '@/components/clients/client-actions';
|
||||||
import { ProjectActions } from '@/components/projects/project-actions';
|
import { ProjectActions } from '@/components/projects/project-actions';
|
||||||
// import { columns } from '@/components/projects/table';
|
|
||||||
import {
|
import {
|
||||||
Accordion,
|
Accordion,
|
||||||
AccordionContent,
|
AccordionContent,
|
||||||
@@ -13,7 +12,6 @@ import {
|
|||||||
import { Alert, AlertDescription, AlertTitle } from '@/components/ui/alert';
|
import { Alert, AlertDescription, AlertTitle } from '@/components/ui/alert';
|
||||||
import { Button } from '@/components/ui/button';
|
import { Button } from '@/components/ui/button';
|
||||||
import { Tooltiper } from '@/components/ui/tooltip';
|
import { Tooltiper } from '@/components/ui/tooltip';
|
||||||
import { useAppParams } from '@/hooks/useAppParams';
|
|
||||||
import { pushModal } from '@/modals';
|
import { pushModal } from '@/modals';
|
||||||
import { InfoIcon, PlusIcon, PlusSquareIcon } from 'lucide-react';
|
import { InfoIcon, PlusIcon, PlusSquareIcon } from 'lucide-react';
|
||||||
|
|
||||||
@@ -24,20 +22,12 @@ interface ListProjectsProps {
|
|||||||
clients: IServiceClientWithProject[];
|
clients: IServiceClientWithProject[];
|
||||||
}
|
}
|
||||||
export default function ListProjects({ projects, clients }: ListProjectsProps) {
|
export default function ListProjects({ projects, clients }: ListProjectsProps) {
|
||||||
const organizationId = useAppParams().organizationId;
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<StickyBelowHeader>
|
<StickyBelowHeader>
|
||||||
<div className="flex items-center justify-between p-4">
|
<div className="flex items-center justify-between p-4">
|
||||||
<div />
|
<div />
|
||||||
<Button
|
<Button icon={PlusIcon} onClick={() => pushModal('AddProject')}>
|
||||||
icon={PlusIcon}
|
|
||||||
onClick={() =>
|
|
||||||
pushModal('AddProject', {
|
|
||||||
organizationId,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<span className="max-sm:hidden">Create project</span>
|
<span className="max-sm:hidden">Create project</span>
|
||||||
<span className="sm:hidden">Project</span>
|
<span className="sm:hidden">Project</span>
|
||||||
</Button>
|
</Button>
|
||||||
@@ -57,7 +47,7 @@ export default function ListProjects({ projects, clients }: ListProjectsProps) {
|
|||||||
<Accordion type="single" collapsible className="-mx-4">
|
<Accordion type="single" collapsible className="-mx-4">
|
||||||
{projects.map((project) => {
|
{projects.map((project) => {
|
||||||
const pClients = clients.filter(
|
const pClients = clients.filter(
|
||||||
(client) => client.project_id === project.id
|
(client) => client.projectId === project.id
|
||||||
);
|
);
|
||||||
return (
|
return (
|
||||||
<AccordionItem
|
<AccordionItem
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
import PageLayout from '@/app/(app)/[organizationSlug]/[projectId]/page-layout';
|
||||||
|
|
||||||
|
import {
|
||||||
|
getClientsByOrganizationSlug,
|
||||||
|
getProjectsByOrganizationSlug,
|
||||||
|
} from '@openpanel/db';
|
||||||
|
|
||||||
|
import ListProjects from './list-projects';
|
||||||
|
|
||||||
|
interface PageProps {
|
||||||
|
params: {
|
||||||
|
organizationSlug: string;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export default async function Page({
|
||||||
|
params: { organizationSlug },
|
||||||
|
}: PageProps) {
|
||||||
|
const [projects, clients] = await Promise.all([
|
||||||
|
getProjectsByOrganizationSlug(organizationSlug),
|
||||||
|
getClientsByOrganizationSlug(organizationSlug),
|
||||||
|
]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<PageLayout title="Projects" organizationSlug={organizationSlug}>
|
||||||
|
<ListProjects projects={projects} clients={clients} />
|
||||||
|
</PageLayout>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
import { StickyBelowHeader } from '@/app/(app)/[organizationId]/[projectId]/layout-sticky-below-header';
|
import { StickyBelowHeader } from '@/app/(app)/[organizationSlug]/[projectId]/layout-sticky-below-header';
|
||||||
import { DataTable } from '@/components/data-table';
|
import { DataTable } from '@/components/data-table';
|
||||||
import { columns } from '@/components/references/table';
|
import { columns } from '@/components/references/table';
|
||||||
import { Button } from '@/components/ui/button';
|
import { Button } from '@/components/ui/button';
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
import PageLayout from '@/app/(app)/[organizationId]/[projectId]/page-layout';
|
import PageLayout from '@/app/(app)/[organizationSlug]/[projectId]/page-layout';
|
||||||
|
|
||||||
import { getReferences } from '@openpanel/db';
|
import { getReferences } from '@openpanel/db';
|
||||||
|
|
||||||
@@ -6,24 +6,24 @@ import ListReferences from './list-references';
|
|||||||
|
|
||||||
interface PageProps {
|
interface PageProps {
|
||||||
params: {
|
params: {
|
||||||
organizationId: string;
|
organizationSlug: string;
|
||||||
projectId: string;
|
projectId: string;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export default async function Page({
|
export default async function Page({
|
||||||
params: { organizationId, projectId },
|
params: { organizationSlug, projectId },
|
||||||
}: PageProps) {
|
}: PageProps) {
|
||||||
const references = await getReferences({
|
const references = await getReferences({
|
||||||
where: {
|
where: {
|
||||||
project_id: projectId,
|
projectId,
|
||||||
},
|
},
|
||||||
take: 50,
|
take: 50,
|
||||||
skip: 0,
|
skip: 0,
|
||||||
});
|
});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<PageLayout title="References" organizationSlug={organizationId}>
|
<PageLayout title="References" organizationSlug={organizationSlug}>
|
||||||
<ListReferences data={references} />
|
<ListReferences data={references} />
|
||||||
</PageLayout>
|
</PageLayout>
|
||||||
);
|
);
|
||||||
@@ -36,7 +36,7 @@ export function CreateProject() {
|
|||||||
const onSubmit: SubmitHandler<IForm> = (values) => {
|
const onSubmit: SubmitHandler<IForm> = (values) => {
|
||||||
mutation.mutate({
|
mutation.mutate({
|
||||||
name: values.name,
|
name: values.name,
|
||||||
organizationId: params.organizationId,
|
organizationSlug: params.organizationSlug,
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -12,14 +12,16 @@ import { CreateProject } from './create-project';
|
|||||||
|
|
||||||
interface PageProps {
|
interface PageProps {
|
||||||
params: {
|
params: {
|
||||||
organizationId: string;
|
organizationSlug: string;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export default async function Page({ params: { organizationId } }: PageProps) {
|
export default async function Page({
|
||||||
|
params: { organizationSlug },
|
||||||
|
}: PageProps) {
|
||||||
const [organization, projects] = await Promise.all([
|
const [organization, projects] = await Promise.all([
|
||||||
getOrganizationBySlug(organizationId),
|
getOrganizationBySlug(organizationSlug),
|
||||||
getCurrentProjects(organizationId),
|
getCurrentProjects(organizationSlug),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
if (!organization) {
|
if (!organization) {
|
||||||
@@ -55,7 +57,7 @@ export default async function Page({ params: { organizationId } }: PageProps) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (projects.length === 1 && projects[0]) {
|
if (projects.length === 1 && projects[0]) {
|
||||||
return redirect(`/${organizationId}/${projects[0].id}`);
|
return redirect(`/${organizationSlug}/${projects[0].id}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
import { StickyBelowHeader } from '@/app/(app)/[organizationId]/[projectId]/layout-sticky-below-header';
|
import { StickyBelowHeader } from '@/app/(app)/[organizationSlug]/[projectId]/layout-sticky-below-header';
|
||||||
import { OverviewReportRange } from '@/app/(app)/[organizationId]/[projectId]/overview-sticky-header';
|
import { OverviewReportRange } from '@/app/(app)/[organizationSlug]/[projectId]/overview-sticky-header';
|
||||||
import { Logo } from '@/components/logo';
|
import { Logo } from '@/components/logo';
|
||||||
import { OverviewFiltersButtons } from '@/components/overview/filters/overview-filters-buttons';
|
import { OverviewFiltersButtons } from '@/components/overview/filters/overview-filters-buttons';
|
||||||
import ServerLiveCounter from '@/components/overview/live-counter';
|
import ServerLiveCounter from '@/components/overview/live-counter';
|
||||||
@@ -28,8 +28,8 @@ export default async function Page({ params: { id } }: PageProps) {
|
|||||||
if (!share.public) {
|
if (!share.public) {
|
||||||
return notFound();
|
return notFound();
|
||||||
}
|
}
|
||||||
const projectId = share.project_id;
|
const projectId = share.projectId;
|
||||||
const organization = await getOrganizationBySlug(share.organization_slug);
|
const organization = await getOrganizationBySlug(share.organizationSlug);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="bg-gradient-to-tl from-blue-950 to-blue-600 p-4 md:p-16">
|
<div className="bg-gradient-to-tl from-blue-950 to-blue-600 p-4 md:p-16">
|
||||||
|
|||||||
@@ -12,9 +12,9 @@ export async function POST(request: Request) {
|
|||||||
data: access
|
data: access
|
||||||
.filter((a): a is string => typeof a === 'string')
|
.filter((a): a is string => typeof a === 'string')
|
||||||
.map((projectId) => ({
|
.map((projectId) => ({
|
||||||
organization_slug: payload.data.organization.slug!,
|
organizationSlug: payload.data.organization.slug!,
|
||||||
project_id: projectId,
|
projectId: projectId,
|
||||||
user_id: payload.data.public_user_data.user_id,
|
userId: payload.data.public_user_data.user_id,
|
||||||
level: AccessLevel.read,
|
level: AccessLevel.read,
|
||||||
})),
|
})),
|
||||||
});
|
});
|
||||||
@@ -23,8 +23,8 @@ export async function POST(request: Request) {
|
|||||||
if (payload.type === 'organizationMembership.deleted') {
|
if (payload.type === 'organizationMembership.deleted') {
|
||||||
await db.projectAccess.deleteMany({
|
await db.projectAccess.deleteMany({
|
||||||
where: {
|
where: {
|
||||||
organization_slug: payload.data.organization.slug!,
|
organizationSlug: payload.data.organization.slug!,
|
||||||
user_id: payload.data.public_user_data.user_id,
|
userId: payload.data.public_user_data.user_id,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -58,9 +58,8 @@ export function OverviewShare({ data }: OverviewShareProps) {
|
|||||||
<DropdownMenuItem
|
<DropdownMenuItem
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
mutation.mutate({
|
mutation.mutate({
|
||||||
|
...data,
|
||||||
public: false,
|
public: false,
|
||||||
projectId: data?.project_id,
|
|
||||||
organizationId: data?.organization_slug,
|
|
||||||
password: null,
|
password: null,
|
||||||
});
|
});
|
||||||
}}
|
}}
|
||||||
|
|||||||
@@ -1,8 +1,7 @@
|
|||||||
import { useParams } from 'next/navigation';
|
import { useParams } from 'next/navigation';
|
||||||
|
|
||||||
// eslint-disable-next-line
|
|
||||||
type AppParams = {
|
type AppParams = {
|
||||||
organizationId: string;
|
organizationSlug: string;
|
||||||
projectId: string;
|
projectId: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -10,7 +9,7 @@ export function useAppParams<T>() {
|
|||||||
const params = useParams<T & AppParams>();
|
const params = useParams<T & AppParams>();
|
||||||
return {
|
return {
|
||||||
...(params ?? {}),
|
...(params ?? {}),
|
||||||
organizationId: params?.organizationId,
|
organizationSlug: params?.organizationSlug,
|
||||||
projectId: params?.projectId,
|
projectId: params?.projectId,
|
||||||
} as T & AppParams;
|
} as T & AppParams;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -43,7 +43,7 @@ interface Props {
|
|||||||
projectId: string;
|
projectId: string;
|
||||||
}
|
}
|
||||||
export default function AddClient(props: Props) {
|
export default function AddClient(props: Props) {
|
||||||
const { organizationId, projectId } = useAppParams();
|
const { organizationSlug, projectId } = useAppParams();
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const form = useForm<IForm>({
|
const form = useForm<IForm>({
|
||||||
resolver: zodResolver(validation),
|
resolver: zodResolver(validation),
|
||||||
@@ -62,14 +62,14 @@ export default function AddClient(props: Props) {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
const query = api.project.list.useQuery({
|
const query = api.project.list.useQuery({
|
||||||
organizationId,
|
organizationSlug,
|
||||||
});
|
});
|
||||||
const onSubmit: SubmitHandler<IForm> = (values) => {
|
const onSubmit: SubmitHandler<IForm> = (values) => {
|
||||||
mutation.mutate({
|
mutation.mutate({
|
||||||
name: values.name,
|
name: values.name,
|
||||||
cors: values.tab === 'website' ? values.cors : null,
|
cors: values.tab === 'website' ? values.cors : null,
|
||||||
projectId: values.projectId,
|
projectId: values.projectId,
|
||||||
organizationId,
|
organizationSlug,
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ const validator = z.object({
|
|||||||
type IForm = z.infer<typeof validator>;
|
type IForm = z.infer<typeof validator>;
|
||||||
|
|
||||||
export default function AddDashboard() {
|
export default function AddDashboard() {
|
||||||
const { projectId, organizationId: organizationSlug } = useAppParams();
|
const { projectId, organizationSlug } = useAppParams();
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
|
|
||||||
const { register, handleSubmit, formState } = useForm<IForm>({
|
const { register, handleSubmit, formState } = useForm<IForm>({
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import { ButtonContainer } from '@/components/button-container';
|
import { ButtonContainer } from '@/components/button-container';
|
||||||
import { InputWithLabel } from '@/components/forms/input-with-label';
|
import { InputWithLabel } from '@/components/forms/input-with-label';
|
||||||
import { Button } from '@/components/ui/button';
|
import { Button } from '@/components/ui/button';
|
||||||
|
import { useAppParams } from '@/hooks/useAppParams';
|
||||||
import { api, handleError } from '@/trpc/client';
|
import { api, handleError } from '@/trpc/client';
|
||||||
import { zodResolver } from '@hookform/resolvers/zod';
|
import { zodResolver } from '@hookform/resolvers/zod';
|
||||||
import { useRouter } from 'next/navigation';
|
import { useRouter } from 'next/navigation';
|
||||||
@@ -16,10 +17,9 @@ const validator = z.object({
|
|||||||
});
|
});
|
||||||
|
|
||||||
type IForm = z.infer<typeof validator>;
|
type IForm = z.infer<typeof validator>;
|
||||||
interface AddProjectProps {
|
|
||||||
organizationId: string;
|
export default function AddProject() {
|
||||||
}
|
const { organizationSlug } = useAppParams();
|
||||||
export default function AddProject({ organizationId }: AddProjectProps) {
|
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const mutation = api.project.create.useMutation({
|
const mutation = api.project.create.useMutation({
|
||||||
onError: handleError,
|
onError: handleError,
|
||||||
@@ -45,7 +45,7 @@ export default function AddProject({ organizationId }: AddProjectProps) {
|
|||||||
onSubmit={handleSubmit((values) => {
|
onSubmit={handleSubmit((values) => {
|
||||||
mutation.mutate({
|
mutation.mutate({
|
||||||
...values,
|
...values,
|
||||||
organizationId,
|
organizationSlug,
|
||||||
});
|
});
|
||||||
})}
|
})}
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -1,12 +1,11 @@
|
|||||||
import { ButtonContainer } from '@/components/button-container';
|
import { ButtonContainer } from '@/components/button-container';
|
||||||
import { InputWithLabel } from '@/components/forms/input-with-label';
|
import { InputWithLabel } from '@/components/forms/input-with-label';
|
||||||
import { Button } from '@/components/ui/button';
|
import { Button } from '@/components/ui/button';
|
||||||
import { Calendar } from '@/components/ui/calendar';
|
|
||||||
import { useAppParams } from '@/hooks/useAppParams';
|
import { useAppParams } from '@/hooks/useAppParams';
|
||||||
import { api, handleError } from '@/trpc/client';
|
import { api, handleError } from '@/trpc/client';
|
||||||
import { zodResolver } from '@hookform/resolvers/zod';
|
import { zodResolver } from '@hookform/resolvers/zod';
|
||||||
import { useRouter } from 'next/navigation';
|
import { useRouter } from 'next/navigation';
|
||||||
import { Controller, useForm } from 'react-hook-form';
|
import { useForm } from 'react-hook-form';
|
||||||
import { toast } from 'sonner';
|
import { toast } from 'sonner';
|
||||||
import type { z } from 'zod';
|
import type { z } from 'zod';
|
||||||
|
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ type EditReportProps = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export default function EditReport({ form, onSubmit }: EditReportProps) {
|
export default function EditReport({ form, onSubmit }: EditReportProps) {
|
||||||
const { register, handleSubmit, reset, formState } = useForm<IForm>({
|
const { register, handleSubmit, formState } = useForm<IForm>({
|
||||||
resolver: zodResolver(validator),
|
resolver: zodResolver(validator),
|
||||||
defaultValues: form,
|
defaultValues: form,
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -8,10 +8,11 @@ import { popModal } from '..';
|
|||||||
|
|
||||||
interface ModalContentProps {
|
interface ModalContentProps {
|
||||||
children: React.ReactNode;
|
children: React.ReactNode;
|
||||||
|
className?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function ModalContent({ children }: ModalContentProps) {
|
export function ModalContent({ children, className }: ModalContentProps) {
|
||||||
return <DialogContent>{children}</DialogContent>;
|
return <DialogContent className={className}>{children}</DialogContent>;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface ModalHeaderProps {
|
interface ModalHeaderProps {
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ type IForm = z.infer<typeof validator>;
|
|||||||
|
|
||||||
export default function SaveReport({ report }: SaveReportProps) {
|
export default function SaveReport({ report }: SaveReportProps) {
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const { organizationId: organizationSlug, projectId } = useAppParams();
|
const { organizationSlug, projectId } = useAppParams();
|
||||||
const searchParams = useSearchParams();
|
const searchParams = useSearchParams();
|
||||||
const dashboardId = searchParams?.get('dashboardId') ?? undefined;
|
const dashboardId = searchParams?.get('dashboardId') ?? undefined;
|
||||||
|
|
||||||
|
|||||||
@@ -20,16 +20,16 @@ const validator = zShareOverview;
|
|||||||
type IForm = z.infer<typeof validator>;
|
type IForm = z.infer<typeof validator>;
|
||||||
|
|
||||||
export default function ShareOverviewModal() {
|
export default function ShareOverviewModal() {
|
||||||
const { projectId, organizationId: organizationSlug } = useAppParams();
|
const { projectId, organizationSlug } = useAppParams();
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
|
|
||||||
const { register, handleSubmit, formState, control } = useForm<IForm>({
|
const { register, handleSubmit, control } = useForm<IForm>({
|
||||||
resolver: zodResolver(validator),
|
resolver: zodResolver(validator),
|
||||||
defaultValues: {
|
defaultValues: {
|
||||||
public: true,
|
public: true,
|
||||||
password: '',
|
password: '',
|
||||||
projectId,
|
projectId,
|
||||||
organizationId: organizationSlug,
|
organizationSlug,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -5,8 +5,13 @@ import dynamic from 'next/dynamic';
|
|||||||
import { createPushModal } from 'pushmodal';
|
import { createPushModal } from 'pushmodal';
|
||||||
|
|
||||||
import type { ConfirmProps } from './Confirm';
|
import type { ConfirmProps } from './Confirm';
|
||||||
|
import { ModalContent } from './Modal/Container';
|
||||||
|
|
||||||
const Loading = () => <Loader className="mb-8 animate-spin" size={40} />;
|
const Loading = () => (
|
||||||
|
<ModalContent className="flex items-center justify-center p-16">
|
||||||
|
<Loader className="animate-spin" size={40} />
|
||||||
|
</ModalContent>
|
||||||
|
);
|
||||||
|
|
||||||
const modals = {
|
const modals = {
|
||||||
EditProject: dynamic(() => import('./EditProject'), {
|
EditProject: dynamic(() => import('./EditProject'), {
|
||||||
|
|||||||
@@ -3,38 +3,9 @@ import { createTRPCRouter, protectedProcedure } from '@/trpc/api/trpc';
|
|||||||
import { z } from 'zod';
|
import { z } from 'zod';
|
||||||
|
|
||||||
import { hashPassword, stripTrailingSlash } from '@openpanel/common';
|
import { hashPassword, stripTrailingSlash } from '@openpanel/common';
|
||||||
import { db, transformClient } from '@openpanel/db';
|
import { db } from '@openpanel/db';
|
||||||
|
|
||||||
export const clientRouter = createTRPCRouter({
|
export const clientRouter = createTRPCRouter({
|
||||||
list: protectedProcedure
|
|
||||||
.input(
|
|
||||||
z.object({
|
|
||||||
organizationId: z.string(),
|
|
||||||
})
|
|
||||||
)
|
|
||||||
.query(async ({ input: { organizationId } }) => {
|
|
||||||
return db.client.findMany({
|
|
||||||
where: {
|
|
||||||
organization_slug: organizationId,
|
|
||||||
},
|
|
||||||
include: {
|
|
||||||
project: true,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}),
|
|
||||||
get: protectedProcedure
|
|
||||||
.input(
|
|
||||||
z.object({
|
|
||||||
id: z.string(),
|
|
||||||
})
|
|
||||||
)
|
|
||||||
.query(({ input }) => {
|
|
||||||
return db.client.findUniqueOrThrow({
|
|
||||||
where: {
|
|
||||||
id: input.id,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}),
|
|
||||||
update: protectedProcedure
|
update: protectedProcedure
|
||||||
.input(
|
.input(
|
||||||
z.object({
|
z.object({
|
||||||
@@ -59,7 +30,7 @@ export const clientRouter = createTRPCRouter({
|
|||||||
z.object({
|
z.object({
|
||||||
name: z.string(),
|
name: z.string(),
|
||||||
projectId: z.string(),
|
projectId: z.string(),
|
||||||
organizationId: z.string(),
|
organizationSlug: z.string(),
|
||||||
cors: z.string().nullable(),
|
cors: z.string().nullable(),
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
@@ -67,8 +38,8 @@ export const clientRouter = createTRPCRouter({
|
|||||||
const secret = randomUUID();
|
const secret = randomUUID();
|
||||||
const client = await db.client.create({
|
const client = await db.client.create({
|
||||||
data: {
|
data: {
|
||||||
organization_slug: input.organizationId,
|
organizationSlug: input.organizationSlug,
|
||||||
project_id: input.projectId,
|
projectId: input.projectId,
|
||||||
name: input.name,
|
name: input.name,
|
||||||
secret: input.cors ? null : await hashPassword(secret),
|
secret: input.cors ? null : await hashPassword(secret),
|
||||||
cors: input.cors ? stripTrailingSlash(input.cors) : '*',
|
cors: input.cors ? stripTrailingSlash(input.cors) : '*',
|
||||||
@@ -76,7 +47,7 @@ export const clientRouter = createTRPCRouter({
|
|||||||
});
|
});
|
||||||
|
|
||||||
return {
|
return {
|
||||||
...transformClient(client),
|
...client,
|
||||||
secret: input.cors ? null : secret,
|
secret: input.cors ? null : secret,
|
||||||
};
|
};
|
||||||
}),
|
}),
|
||||||
|
|||||||
@@ -28,8 +28,8 @@ export const dashboardRouter = createTRPCRouter({
|
|||||||
return db.dashboard.create({
|
return db.dashboard.create({
|
||||||
data: {
|
data: {
|
||||||
id: await getId('dashboard', name),
|
id: await getId('dashboard', name),
|
||||||
project_id: projectId,
|
projectId: projectId,
|
||||||
organization_slug: organizationSlug,
|
organizationSlug: organizationSlug,
|
||||||
name,
|
name,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
@@ -63,7 +63,7 @@ export const dashboardRouter = createTRPCRouter({
|
|||||||
if (forceDelete) {
|
if (forceDelete) {
|
||||||
await db.report.deleteMany({
|
await db.report.deleteMany({
|
||||||
where: {
|
where: {
|
||||||
dashboard_id: id,
|
dashboardId: id,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,12 +22,12 @@ export const eventRouter = createTRPCRouter({
|
|||||||
.mutation(({ input: { projectId, name, icon, color, conversion } }) => {
|
.mutation(({ input: { projectId, name, icon, color, conversion } }) => {
|
||||||
return db.eventMeta.upsert({
|
return db.eventMeta.upsert({
|
||||||
where: {
|
where: {
|
||||||
name_project_id: {
|
name_projectId: {
|
||||||
name,
|
name,
|
||||||
project_id: projectId,
|
projectId,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
create: { project_id: projectId, name, icon, color, conversion },
|
create: { projectId, name, icon, color, conversion },
|
||||||
update: { icon, color, conversion },
|
update: { icon, color, conversion },
|
||||||
});
|
});
|
||||||
}),
|
}),
|
||||||
|
|||||||
@@ -4,12 +4,7 @@ import { clerkClient } from '@clerk/nextjs';
|
|||||||
import { z } from 'zod';
|
import { z } from 'zod';
|
||||||
|
|
||||||
import { hashPassword, stripTrailingSlash } from '@openpanel/common';
|
import { hashPassword, stripTrailingSlash } from '@openpanel/common';
|
||||||
import {
|
import { db, transformOrganization } from '@openpanel/db';
|
||||||
db,
|
|
||||||
transformClient,
|
|
||||||
transformOrganization,
|
|
||||||
transformProject,
|
|
||||||
} from '@openpanel/db';
|
|
||||||
|
|
||||||
export const onboardingRouter = createTRPCRouter({
|
export const onboardingRouter = createTRPCRouter({
|
||||||
organziation: protectedProcedure
|
organziation: protectedProcedure
|
||||||
@@ -30,7 +25,7 @@ export const onboardingRouter = createTRPCRouter({
|
|||||||
const project = await db.project.create({
|
const project = await db.project.create({
|
||||||
data: {
|
data: {
|
||||||
name: input.project,
|
name: input.project,
|
||||||
organization_slug: org.slug,
|
organizationSlug: org.slug,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -38,19 +33,19 @@ export const onboardingRouter = createTRPCRouter({
|
|||||||
const client = await db.client.create({
|
const client = await db.client.create({
|
||||||
data: {
|
data: {
|
||||||
name: `${project.name} Client`,
|
name: `${project.name} Client`,
|
||||||
organization_slug: org.slug,
|
organizationSlug: org.slug,
|
||||||
project_id: project.id,
|
projectId: project.id,
|
||||||
cors: input.cors ? stripTrailingSlash(input.cors) : '*',
|
cors: input.cors ? stripTrailingSlash(input.cors) : '*',
|
||||||
secret: input.cors ? null : await hashPassword(secret),
|
secret: input.cors ? null : await hashPassword(secret),
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
return {
|
return {
|
||||||
client: transformClient({
|
client: {
|
||||||
...client,
|
...client,
|
||||||
secret: input.cors ? null : secret,
|
secret: input.cors ? null : secret,
|
||||||
}),
|
},
|
||||||
project: transformProject(project),
|
project,
|
||||||
organization: transformOrganization(org),
|
organization: transformOrganization(org),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -77,15 +77,15 @@ export const organizationRouter = createTRPCRouter({
|
|||||||
return db.$transaction([
|
return db.$transaction([
|
||||||
db.projectAccess.deleteMany({
|
db.projectAccess.deleteMany({
|
||||||
where: {
|
where: {
|
||||||
user_id: input.userId,
|
userId: input.userId,
|
||||||
organization_slug: input.organizationSlug,
|
organizationSlug: input.organizationSlug,
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
db.projectAccess.createMany({
|
db.projectAccess.createMany({
|
||||||
data: input.access.map((projectId) => ({
|
data: input.access.map((projectId) => ({
|
||||||
user_id: input.userId,
|
userId: input.userId,
|
||||||
organization_slug: input.organizationSlug,
|
organizationSlug: input.organizationSlug,
|
||||||
project_id: projectId,
|
projectId: projectId,
|
||||||
level: 'read',
|
level: 'read',
|
||||||
})),
|
})),
|
||||||
}),
|
}),
|
||||||
|
|||||||
@@ -8,12 +8,12 @@ export const projectRouter = createTRPCRouter({
|
|||||||
list: protectedProcedure
|
list: protectedProcedure
|
||||||
.input(
|
.input(
|
||||||
z.object({
|
z.object({
|
||||||
organizationId: z.string().nullable(),
|
organizationSlug: z.string().nullable(),
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
.query(async ({ input: { organizationId } }) => {
|
.query(async ({ input: { organizationSlug } }) => {
|
||||||
if (organizationId === null) return [];
|
if (organizationSlug === null) return [];
|
||||||
return getProjectsByOrganizationSlug(organizationId);
|
return getProjectsByOrganizationSlug(organizationSlug);
|
||||||
}),
|
}),
|
||||||
|
|
||||||
update: protectedProcedure
|
update: protectedProcedure
|
||||||
@@ -37,15 +37,15 @@ export const projectRouter = createTRPCRouter({
|
|||||||
.input(
|
.input(
|
||||||
z.object({
|
z.object({
|
||||||
name: z.string().min(1),
|
name: z.string().min(1),
|
||||||
organizationId: z.string(),
|
organizationSlug: z.string(),
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
.mutation(async ({ input }) => {
|
.mutation(async ({ input: { name, organizationSlug } }) => {
|
||||||
return db.project.create({
|
return db.project.create({
|
||||||
data: {
|
data: {
|
||||||
id: await getId('project', input.name),
|
id: await getId('project', name),
|
||||||
organization_slug: input.organizationId,
|
organizationSlug: organizationSlug,
|
||||||
name: input.name,
|
name: name,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
}),
|
}),
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ export const referenceRouter = createTRPCRouter({
|
|||||||
data: {
|
data: {
|
||||||
title,
|
title,
|
||||||
description,
|
description,
|
||||||
project_id: projectId,
|
projectId,
|
||||||
date: new Date(datetime),
|
date: new Date(datetime),
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
@@ -47,7 +47,7 @@ export const referenceRouter = createTRPCRouter({
|
|||||||
const { startDate, endDate } = getChartStartEndDate(input);
|
const { startDate, endDate } = getChartStartEndDate(input);
|
||||||
return getReferences({
|
return getReferences({
|
||||||
where: {
|
where: {
|
||||||
project_id: projectId,
|
projectId,
|
||||||
date: {
|
date: {
|
||||||
gte: new Date(startDate),
|
gte: new Date(startDate),
|
||||||
lte: new Date(endDate),
|
lte: new Date(endDate),
|
||||||
|
|||||||
@@ -20,14 +20,14 @@ export const reportRouter = createTRPCRouter({
|
|||||||
});
|
});
|
||||||
return db.report.create({
|
return db.report.create({
|
||||||
data: {
|
data: {
|
||||||
project_id: dashboard.project_id,
|
projectId: dashboard.projectId,
|
||||||
dashboard_id: dashboardId,
|
dashboardId,
|
||||||
name: report.name,
|
name: report.name,
|
||||||
events: report.events,
|
events: report.events,
|
||||||
interval: report.interval,
|
interval: report.interval,
|
||||||
breakdowns: report.breakdowns,
|
breakdowns: report.breakdowns,
|
||||||
chart_type: report.chartType,
|
chartType: report.chartType,
|
||||||
line_type: report.lineType,
|
lineType: report.lineType,
|
||||||
range: report.range,
|
range: report.range,
|
||||||
formula: report.formula,
|
formula: report.formula,
|
||||||
},
|
},
|
||||||
@@ -50,8 +50,8 @@ export const reportRouter = createTRPCRouter({
|
|||||||
events: report.events,
|
events: report.events,
|
||||||
interval: report.interval,
|
interval: report.interval,
|
||||||
breakdowns: report.breakdowns,
|
breakdowns: report.breakdowns,
|
||||||
chart_type: report.chartType,
|
chartType: report.chartType,
|
||||||
line_type: report.lineType,
|
lineType: report.lineType,
|
||||||
range: report.range,
|
range: report.range,
|
||||||
formula: report.formula,
|
formula: report.formula,
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -12,12 +12,12 @@ export const shareRouter = createTRPCRouter({
|
|||||||
.mutation(({ input }) => {
|
.mutation(({ input }) => {
|
||||||
return db.shareOverview.upsert({
|
return db.shareOverview.upsert({
|
||||||
where: {
|
where: {
|
||||||
project_id: input.projectId,
|
projectId: input.projectId,
|
||||||
},
|
},
|
||||||
create: {
|
create: {
|
||||||
id: uid.rnd(),
|
id: uid.rnd(),
|
||||||
organization_slug: input.organizationId,
|
organizationSlug: input.organizationSlug,
|
||||||
project_id: input.projectId,
|
projectId: input.projectId,
|
||||||
public: input.public,
|
public: input.public,
|
||||||
password: input.password || null,
|
password: input.password || null,
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -4,11 +4,10 @@ import Image from 'next/image';
|
|||||||
import Link from 'next/link';
|
import Link from 'next/link';
|
||||||
|
|
||||||
import { Heading2, Lead2 } from './copy';
|
import { Heading2, Lead2 } from './copy';
|
||||||
import { JoinWaitlist } from './join-waitlist';
|
|
||||||
|
|
||||||
export default function Footer() {
|
export default function Footer() {
|
||||||
return (
|
return (
|
||||||
<footer className="bg-blue-darker relative relative mt-40 text-white">
|
<footer className="bg-blue-darker relative mt-40 text-white">
|
||||||
<div className="absolute inset-0 h-full w-full bg-[radial-gradient(circle,rgba(255,255,255,0.2)_0%,rgba(255,255,255,0)_100%)]"></div>
|
<div className="absolute inset-0 h-full w-full bg-[radial-gradient(circle,rgba(255,255,255,0.2)_0%,rgba(255,255,255,0)_100%)]"></div>
|
||||||
<div className="container relative flex flex-col items-center text-center">
|
<div className="container relative flex flex-col items-center text-center">
|
||||||
<div className="my-24">
|
<div className="my-24">
|
||||||
|
|||||||
@@ -0,0 +1,97 @@
|
|||||||
|
-- Project
|
||||||
|
-- organization_slug -> organizationSlug
|
||||||
|
ALTER TABLE
|
||||||
|
IF EXISTS "projects" RENAME COLUMN "organization_slug" TO "organizationSlug";
|
||||||
|
|
||||||
|
-- ProjectAccess
|
||||||
|
-- project_id -> projectId
|
||||||
|
-- organization_slug -> organizationSlug
|
||||||
|
-- user_id -> userId
|
||||||
|
ALTER TABLE
|
||||||
|
IF EXISTS "project_access" RENAME COLUMN "project_id" TO "projectId";
|
||||||
|
|
||||||
|
ALTER TABLE
|
||||||
|
IF EXISTS "project_access" RENAME COLUMN "organization_slug" TO "organizationSlug";
|
||||||
|
|
||||||
|
ALTER TABLE
|
||||||
|
IF EXISTS "project_access" RENAME COLUMN "user_id" TO "userId";
|
||||||
|
|
||||||
|
-- Event
|
||||||
|
-- project_id -> projectId
|
||||||
|
-- profile_id -> profileId
|
||||||
|
ALTER TABLE
|
||||||
|
IF EXISTS "events" RENAME COLUMN "project_id" TO "projectId";
|
||||||
|
|
||||||
|
ALTER TABLE
|
||||||
|
IF EXISTS "events" RENAME COLUMN "profile_id" TO "profileId";
|
||||||
|
|
||||||
|
-- Profile
|
||||||
|
-- external_id -> externalId
|
||||||
|
-- first_name -> firstName
|
||||||
|
-- last_name -> lastName
|
||||||
|
-- project_id -> projectId
|
||||||
|
ALTER TABLE
|
||||||
|
IF EXISTS "profiles" RENAME COLUMN "external_id" TO "externalId";
|
||||||
|
|
||||||
|
ALTER TABLE
|
||||||
|
IF EXISTS "profiles" RENAME COLUMN "first_name" TO "firstName";
|
||||||
|
|
||||||
|
ALTER TABLE
|
||||||
|
IF EXISTS "profiles" RENAME COLUMN "last_name" TO "lastName";
|
||||||
|
|
||||||
|
ALTER TABLE
|
||||||
|
IF EXISTS "profiles" RENAME COLUMN "project_id" TO "projectId";
|
||||||
|
|
||||||
|
-- Client
|
||||||
|
-- project_id -> projectId
|
||||||
|
-- organization_slug -> organizationSlug
|
||||||
|
ALTER TABLE
|
||||||
|
IF EXISTS "clients" RENAME COLUMN "project_id" TO "projectId";
|
||||||
|
|
||||||
|
ALTER TABLE
|
||||||
|
IF EXISTS "clients" RENAME COLUMN "organization_slug" TO "organizationSlug";
|
||||||
|
|
||||||
|
-- Dashboard
|
||||||
|
-- organization_slug -> organizationSlug
|
||||||
|
-- project_id -> projectId
|
||||||
|
ALTER TABLE
|
||||||
|
IF EXISTS "dashboards" RENAME COLUMN "organization_slug" TO "organizationSlug";
|
||||||
|
|
||||||
|
ALTER TABLE
|
||||||
|
IF EXISTS "dashboards" RENAME COLUMN "project_id" TO "projectId";
|
||||||
|
|
||||||
|
-- Report
|
||||||
|
-- chart_type -> chartType
|
||||||
|
-- line_type -> lineType
|
||||||
|
-- project_id -> projectId
|
||||||
|
-- dashboard_id -> dashboardId
|
||||||
|
ALTER TABLE
|
||||||
|
IF EXISTS "reports" RENAME COLUMN "chart_type" TO "chartType";
|
||||||
|
|
||||||
|
ALTER TABLE
|
||||||
|
IF EXISTS "reports" RENAME COLUMN "line_type" TO "lineType";
|
||||||
|
|
||||||
|
ALTER TABLE
|
||||||
|
IF EXISTS "reports" RENAME COLUMN "project_id" TO "projectId";
|
||||||
|
|
||||||
|
ALTER TABLE
|
||||||
|
IF EXISTS "reports" RENAME COLUMN "dashboard_id" TO "dashboardId";
|
||||||
|
|
||||||
|
-- ShareOverview
|
||||||
|
-- project_id -> projectId
|
||||||
|
-- organization_slug -> organizationSlug
|
||||||
|
ALTER TABLE
|
||||||
|
IF EXISTS "shares" RENAME COLUMN "project_id" TO "projectId";
|
||||||
|
|
||||||
|
ALTER TABLE
|
||||||
|
IF EXISTS "shares" RENAME COLUMN "organization_slug" TO "organizationSlug";
|
||||||
|
|
||||||
|
-- EventMeta (ta bort constraint)
|
||||||
|
-- project_id -> projectId
|
||||||
|
ALTER TABLE
|
||||||
|
IF EXISTS "event_meta" RENAME COLUMN "project_id" TO "projectId";
|
||||||
|
|
||||||
|
-- Reference
|
||||||
|
-- project_id -> projectId
|
||||||
|
ALTER TABLE
|
||||||
|
IF EXISTS "references" RENAME COLUMN "project_id" TO "projectId";
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
-- RenameForeignKey
|
||||||
|
ALTER TABLE "clients" RENAME CONSTRAINT "clients_project_id_fkey" TO "clients_projectId_fkey";
|
||||||
|
|
||||||
|
-- RenameForeignKey
|
||||||
|
ALTER TABLE "dashboards" RENAME CONSTRAINT "dashboards_project_id_fkey" TO "dashboards_projectId_fkey";
|
||||||
|
|
||||||
|
-- RenameForeignKey
|
||||||
|
ALTER TABLE "event_meta" RENAME CONSTRAINT "event_meta_project_id_fkey" TO "event_meta_projectId_fkey";
|
||||||
|
|
||||||
|
-- RenameForeignKey
|
||||||
|
ALTER TABLE "events" RENAME CONSTRAINT "events_project_id_fkey" TO "events_projectId_fkey";
|
||||||
|
|
||||||
|
-- RenameForeignKey
|
||||||
|
ALTER TABLE "profiles" RENAME CONSTRAINT "profiles_project_id_fkey" TO "profiles_projectId_fkey";
|
||||||
|
|
||||||
|
-- RenameForeignKey
|
||||||
|
ALTER TABLE "project_access" RENAME CONSTRAINT "project_access_project_id_fkey" TO "project_access_projectId_fkey";
|
||||||
|
|
||||||
|
-- RenameForeignKey
|
||||||
|
ALTER TABLE "references" RENAME CONSTRAINT "references_project_id_fkey" TO "references_projectId_fkey";
|
||||||
|
|
||||||
|
-- RenameForeignKey
|
||||||
|
ALTER TABLE "reports" RENAME CONSTRAINT "reports_dashboard_id_fkey" TO "reports_dashboardId_fkey";
|
||||||
|
|
||||||
|
-- RenameForeignKey
|
||||||
|
ALTER TABLE "reports" RENAME CONSTRAINT "reports_project_id_fkey" TO "reports_projectId_fkey";
|
||||||
|
|
||||||
|
-- RenameForeignKey
|
||||||
|
ALTER TABLE "shares" RENAME CONSTRAINT "shares_project_id_fkey" TO "shares_projectId_fkey";
|
||||||
|
|
||||||
|
-- RenameIndex
|
||||||
|
ALTER INDEX "event_meta_name_project_id_key" RENAME TO "event_meta_name_projectId_key";
|
||||||
|
|
||||||
|
-- RenameIndex
|
||||||
|
ALTER INDEX "shares_project_id_key" RENAME TO "shares_projectId_key";
|
||||||
@@ -13,22 +13,22 @@ datasource db {
|
|||||||
model Project {
|
model Project {
|
||||||
id String @id @default(dbgenerated("gen_random_uuid()"))
|
id String @id @default(dbgenerated("gen_random_uuid()"))
|
||||||
name String
|
name String
|
||||||
organization_slug String
|
organizationSlug String
|
||||||
events Event[]
|
|
||||||
eventsCount Int @default(0)
|
eventsCount Int @default(0)
|
||||||
|
|
||||||
|
events Event[]
|
||||||
profiles Profile[]
|
profiles Profile[]
|
||||||
clients Client[]
|
clients Client[]
|
||||||
|
|
||||||
createdAt DateTime @default(now())
|
|
||||||
updatedAt DateTime @default(now()) @updatedAt
|
|
||||||
reports Report[]
|
reports Report[]
|
||||||
dashboards Dashboard[]
|
dashboards Dashboard[]
|
||||||
share ShareOverview?
|
share ShareOverview?
|
||||||
EventMeta EventMeta[]
|
meta EventMeta[]
|
||||||
Reference Reference[]
|
references Reference[]
|
||||||
|
|
||||||
access ProjectAccess[]
|
access ProjectAccess[]
|
||||||
|
|
||||||
|
createdAt DateTime @default(now())
|
||||||
|
updatedAt DateTime @default(now()) @updatedAt
|
||||||
|
|
||||||
@@map("projects")
|
@@map("projects")
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -40,10 +40,10 @@ enum AccessLevel {
|
|||||||
|
|
||||||
model ProjectAccess {
|
model ProjectAccess {
|
||||||
id String @id @default(dbgenerated("gen_random_uuid()")) @db.Uuid
|
id String @id @default(dbgenerated("gen_random_uuid()")) @db.Uuid
|
||||||
project_id String
|
projectId String
|
||||||
project Project @relation(fields: [project_id], references: [id])
|
project Project @relation(fields: [projectId], references: [id])
|
||||||
organization_slug String
|
organizationSlug String
|
||||||
user_id String
|
userId String
|
||||||
level AccessLevel
|
level AccessLevel
|
||||||
createdAt DateTime @default(now())
|
createdAt DateTime @default(now())
|
||||||
updatedAt DateTime @default(now()) @updatedAt
|
updatedAt DateTime @default(now()) @updatedAt
|
||||||
@@ -55,10 +55,10 @@ model Event {
|
|||||||
id String @id @default(dbgenerated("gen_random_uuid()")) @db.Uuid
|
id String @id @default(dbgenerated("gen_random_uuid()")) @db.Uuid
|
||||||
name String
|
name String
|
||||||
properties Json
|
properties Json
|
||||||
project_id String
|
projectId String
|
||||||
project Project @relation(fields: [project_id], references: [id])
|
project Project @relation(fields: [projectId], references: [id])
|
||||||
|
|
||||||
profile_id String?
|
profileId String?
|
||||||
|
|
||||||
createdAt DateTime @default(now())
|
createdAt DateTime @default(now())
|
||||||
updatedAt DateTime @default(now()) @updatedAt
|
updatedAt DateTime @default(now()) @updatedAt
|
||||||
@@ -76,14 +76,14 @@ model Salt {
|
|||||||
|
|
||||||
model Profile {
|
model Profile {
|
||||||
id String @id
|
id String @id
|
||||||
external_id String?
|
externalId String?
|
||||||
first_name String?
|
firstName String?
|
||||||
last_name String?
|
lastName String?
|
||||||
email String?
|
email String?
|
||||||
avatar String?
|
avatar String?
|
||||||
properties Json
|
properties Json
|
||||||
project_id String
|
projectId String
|
||||||
project Project @relation(fields: [project_id], references: [id])
|
project Project @relation(fields: [projectId], references: [id])
|
||||||
createdAt DateTime @default(now())
|
createdAt DateTime @default(now())
|
||||||
updatedAt DateTime @default(now()) @updatedAt
|
updatedAt DateTime @default(now()) @updatedAt
|
||||||
|
|
||||||
@@ -94,9 +94,9 @@ model Client {
|
|||||||
id String @id @default(dbgenerated("gen_random_uuid()")) @db.Uuid
|
id String @id @default(dbgenerated("gen_random_uuid()")) @db.Uuid
|
||||||
name String
|
name String
|
||||||
secret String?
|
secret String?
|
||||||
project_id String
|
projectId String
|
||||||
project Project @relation(fields: [project_id], references: [id])
|
project Project @relation(fields: [projectId], references: [id])
|
||||||
organization_slug String
|
organizationSlug String
|
||||||
cors String @default("*")
|
cors String @default("*")
|
||||||
|
|
||||||
createdAt DateTime @default(now())
|
createdAt DateTime @default(now())
|
||||||
@@ -126,9 +126,9 @@ enum ChartType {
|
|||||||
model Dashboard {
|
model Dashboard {
|
||||||
id String @id @default(dbgenerated("gen_random_uuid()"))
|
id String @id @default(dbgenerated("gen_random_uuid()"))
|
||||||
name String
|
name String
|
||||||
organization_slug String
|
organizationSlug String
|
||||||
project_id String
|
projectId String
|
||||||
project Project @relation(fields: [project_id], references: [id])
|
project Project @relation(fields: [projectId], references: [id])
|
||||||
reports Report[]
|
reports Report[]
|
||||||
|
|
||||||
createdAt DateTime @default(now())
|
createdAt DateTime @default(now())
|
||||||
@@ -149,19 +149,19 @@ model Report {
|
|||||||
name String
|
name String
|
||||||
interval Interval
|
interval Interval
|
||||||
range String @default("1m")
|
range String @default("1m")
|
||||||
chart_type ChartType
|
chartType ChartType
|
||||||
line_type String @default("monotone")
|
lineType String @default("monotone")
|
||||||
breakdowns Json
|
breakdowns Json
|
||||||
events Json
|
events Json
|
||||||
formula String?
|
formula String?
|
||||||
unit String?
|
unit String?
|
||||||
metric Metric @default(sum)
|
metric Metric @default(sum)
|
||||||
project_id String
|
projectId String
|
||||||
project Project @relation(fields: [project_id], references: [id])
|
project Project @relation(fields: [projectId], references: [id])
|
||||||
previous Boolean @default(false)
|
previous Boolean @default(false)
|
||||||
|
|
||||||
dashboard_id String
|
dashboardId String
|
||||||
dashboard Dashboard @relation(fields: [dashboard_id], references: [id])
|
dashboard Dashboard @relation(fields: [dashboardId], references: [id])
|
||||||
|
|
||||||
createdAt DateTime @default(now())
|
createdAt DateTime @default(now())
|
||||||
updatedAt DateTime @default(now()) @updatedAt
|
updatedAt DateTime @default(now()) @updatedAt
|
||||||
@@ -181,9 +181,9 @@ model Waitlist {
|
|||||||
|
|
||||||
model ShareOverview {
|
model ShareOverview {
|
||||||
id String @unique
|
id String @unique
|
||||||
project_id String @unique
|
projectId String @unique
|
||||||
project Project @relation(fields: [project_id], references: [id])
|
project Project @relation(fields: [projectId], references: [id])
|
||||||
organization_slug String
|
organizationSlug String
|
||||||
public Boolean @default(false)
|
public Boolean @default(false)
|
||||||
password String?
|
password String?
|
||||||
createdAt DateTime @default(now())
|
createdAt DateTime @default(now())
|
||||||
@@ -198,13 +198,13 @@ model EventMeta {
|
|||||||
conversion Boolean?
|
conversion Boolean?
|
||||||
color String?
|
color String?
|
||||||
icon String?
|
icon String?
|
||||||
project_id String
|
projectId String
|
||||||
project Project @relation(fields: [project_id], references: [id])
|
project Project @relation(fields: [projectId], references: [id])
|
||||||
|
|
||||||
createdAt DateTime @default(now())
|
createdAt DateTime @default(now())
|
||||||
updatedAt DateTime @default(now()) @updatedAt
|
updatedAt DateTime @default(now()) @updatedAt
|
||||||
|
|
||||||
@@unique([name, project_id])
|
@@unique([name, projectId])
|
||||||
@@map("event_meta")
|
@@map("event_meta")
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -213,8 +213,8 @@ model Reference {
|
|||||||
title String
|
title String
|
||||||
description String?
|
description String?
|
||||||
date DateTime @default(now())
|
date DateTime @default(now())
|
||||||
project_id String
|
projectId String
|
||||||
project Project @relation(fields: [project_id], references: [id])
|
project Project @relation(fields: [projectId], references: [id])
|
||||||
|
|
||||||
createdAt DateTime @default(now())
|
createdAt DateTime @default(now())
|
||||||
updatedAt DateTime @default(now()) @updatedAt
|
updatedAt DateTime @default(now()) @updatedAt
|
||||||
|
|||||||
@@ -1,38 +1,20 @@
|
|||||||
import type { Client } from '../prisma-client';
|
import type { Client, Prisma } from '../prisma-client';
|
||||||
import { db } from '../prisma-client';
|
import { db } from '../prisma-client';
|
||||||
import { transformProject } from './project.service';
|
|
||||||
import type { IServiceProject } from './project.service';
|
|
||||||
|
|
||||||
export type IServiceClient = ReturnType<typeof transformClient>;
|
export type IServiceClient = Client;
|
||||||
export type IServiceClientWithProject = IServiceClient & {
|
export type IServiceClientWithProject = Prisma.ClientGetPayload<{
|
||||||
project: Exclude<IServiceProject, null>;
|
include: {
|
||||||
|
project: true;
|
||||||
};
|
};
|
||||||
|
}>;
|
||||||
|
|
||||||
export function transformClient({ organization_slug, ...client }: Client) {
|
export async function getClientsByOrganizationSlug(organizationSlug: string) {
|
||||||
return {
|
return db.client.findMany({
|
||||||
...client,
|
|
||||||
organizationSlug: organization_slug,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function getClientsByOrganizationId(organizationId: string) {
|
|
||||||
const clients = await db.client.findMany({
|
|
||||||
where: {
|
where: {
|
||||||
organization_slug: organizationId,
|
organizationSlug,
|
||||||
},
|
},
|
||||||
include: {
|
include: {
|
||||||
project: true,
|
project: true,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
return clients
|
|
||||||
.map((client) => {
|
|
||||||
return {
|
|
||||||
...transformClient(client),
|
|
||||||
project: transformProject(client.project),
|
|
||||||
};
|
|
||||||
})
|
|
||||||
.filter(
|
|
||||||
(client): client is IServiceClientWithProject => client.project !== null
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,15 +1,18 @@
|
|||||||
|
import type { Dashboard, Prisma } from '../prisma-client';
|
||||||
import { db } from '../prisma-client';
|
import { db } from '../prisma-client';
|
||||||
|
|
||||||
export type IServiceDashboard = Awaited<ReturnType<typeof getDashboardById>>;
|
export type IServiceDashboard = Dashboard;
|
||||||
export type IServiceDashboards = Awaited<
|
export type IServiceDashboards = Prisma.DashboardGetPayload<{
|
||||||
ReturnType<typeof getDashboardsByProjectId>
|
include: {
|
||||||
>;
|
project: true;
|
||||||
|
};
|
||||||
|
}>[];
|
||||||
|
|
||||||
export async function getDashboardById(id: string, projectId: string) {
|
export async function getDashboardById(id: string, projectId: string) {
|
||||||
const dashboard = await db.dashboard.findUnique({
|
const dashboard = await db.dashboard.findUnique({
|
||||||
where: {
|
where: {
|
||||||
id,
|
id,
|
||||||
project_id: projectId,
|
projectId,
|
||||||
},
|
},
|
||||||
include: {
|
include: {
|
||||||
project: true,
|
project: true,
|
||||||
@@ -26,7 +29,7 @@ export async function getDashboardById(id: string, projectId: string) {
|
|||||||
export function getDashboardsByProjectId(projectId: string) {
|
export function getDashboardsByProjectId(projectId: string) {
|
||||||
return db.dashboard.findMany({
|
return db.dashboard.findMany({
|
||||||
where: {
|
where: {
|
||||||
project_id: projectId,
|
projectId,
|
||||||
},
|
},
|
||||||
include: {
|
include: {
|
||||||
project: true,
|
project: true,
|
||||||
|
|||||||
@@ -144,7 +144,7 @@ export async function getEvents(
|
|||||||
name: {
|
name: {
|
||||||
in: names,
|
in: names,
|
||||||
},
|
},
|
||||||
project_id: events[0]?.project_id,
|
projectId: events[0]?.project_id,
|
||||||
},
|
},
|
||||||
select: options.meta === true ? undefined : options.meta,
|
select: options.meta === true ? undefined : options.meta,
|
||||||
});
|
});
|
||||||
@@ -265,7 +265,7 @@ export async function getEventList({
|
|||||||
sb.where.projectId = `project_id = ${escape(projectId)}`;
|
sb.where.projectId = `project_id = ${escape(projectId)}`;
|
||||||
|
|
||||||
if (profileId) {
|
if (profileId) {
|
||||||
sb.where.deviceId = `device_id IN (SELECT device_id as did FROM openpanel.events WHERE profile_id = ${escape(profileId)} group by did)`;
|
sb.where.deviceId = `device_id IN (SELECT device_id as did FROM events WHERE profile_id = ${escape(profileId)} group by did)`;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (events && events.length > 0) {
|
if (events && events.length > 0) {
|
||||||
@@ -357,7 +357,7 @@ export function createBotEvent({
|
|||||||
export function getConversionEventNames(projectId: string) {
|
export function getConversionEventNames(projectId: string) {
|
||||||
return db.eventMeta.findMany({
|
return db.eventMeta.findMany({
|
||||||
where: {
|
where: {
|
||||||
project_id: projectId,
|
projectId,
|
||||||
conversion: true,
|
conversion: true,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ import { db } from '../prisma-client';
|
|||||||
export type IServiceOrganization = ReturnType<typeof transformOrganization>;
|
export type IServiceOrganization = ReturnType<typeof transformOrganization>;
|
||||||
export type IServiceInvite = ReturnType<typeof transformInvite>;
|
export type IServiceInvite = ReturnType<typeof transformInvite>;
|
||||||
export type IServiceMember = ReturnType<typeof transformMember>;
|
export type IServiceMember = ReturnType<typeof transformMember>;
|
||||||
export type IServiceProjectAccess = ReturnType<typeof transformAccess>;
|
export type IServiceProjectAccess = ProjectAccess;
|
||||||
|
|
||||||
export function transformOrganization(org: Organization) {
|
export function transformOrganization(org: Organization) {
|
||||||
return {
|
return {
|
||||||
@@ -21,15 +21,6 @@ export function transformOrganization(org: Organization) {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export function transformAccess(access: ProjectAccess) {
|
|
||||||
return {
|
|
||||||
projectId: access.project_id,
|
|
||||||
userId: access.user_id,
|
|
||||||
level: access.level,
|
|
||||||
organizationSlug: access.organization_slug,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function getCurrentOrganizations() {
|
export async function getCurrentOrganizations() {
|
||||||
const session = auth();
|
const session = auth();
|
||||||
const organizations = await clerkClient.users.getOrganizationMembershipList({
|
const organizations = await clerkClient.users.getOrganizationMembershipList({
|
||||||
@@ -53,7 +44,7 @@ export async function getOrganizationByProjectId(projectId: string) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
return clerkClient.organizations.getOrganization({
|
return clerkClient.organizations.getOrganization({
|
||||||
slug: project.organization_slug,
|
slug: project.organizationSlug,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -110,7 +101,7 @@ export async function getMembers(organizationSlug: string) {
|
|||||||
}),
|
}),
|
||||||
db.projectAccess.findMany({
|
db.projectAccess.findMany({
|
||||||
where: {
|
where: {
|
||||||
organization_slug: organizationSlug,
|
organizationSlug,
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
]);
|
]);
|
||||||
@@ -118,11 +109,11 @@ export async function getMembers(organizationSlug: string) {
|
|||||||
return members
|
return members
|
||||||
.map((member) => {
|
.map((member) => {
|
||||||
const projectAccess = access.filter(
|
const projectAccess = access.filter(
|
||||||
(item) => item.user_id === member.publicUserData?.userId
|
(item) => item.userId === member.publicUserData?.userId
|
||||||
);
|
);
|
||||||
return {
|
return {
|
||||||
...member,
|
...member,
|
||||||
access: projectAccess.map(transformAccess),
|
access: projectAccess,
|
||||||
};
|
};
|
||||||
})
|
})
|
||||||
.map(transformMember);
|
.map(transformMember);
|
||||||
|
|||||||
@@ -1,17 +1,9 @@
|
|||||||
import { auth } from '@clerk/nextjs';
|
import { auth } from '@clerk/nextjs';
|
||||||
import { project } from 'ramda';
|
|
||||||
|
|
||||||
import type { Project } from '../prisma-client';
|
import type { Project } from '../prisma-client';
|
||||||
import { db } from '../prisma-client';
|
import { db } from '../prisma-client';
|
||||||
|
|
||||||
export type IServiceProject = ReturnType<typeof transformProject>;
|
export type IServiceProject = Project;
|
||||||
|
|
||||||
export function transformProject({ organization_slug, ...project }: Project) {
|
|
||||||
return {
|
|
||||||
organizationSlug: organization_slug,
|
|
||||||
...project,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function getProjectById(id: string) {
|
export async function getProjectById(id: string) {
|
||||||
const res = await db.project.findUnique({
|
const res = await db.project.findUnique({
|
||||||
@@ -24,46 +16,32 @@ export async function getProjectById(id: string) {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
return transformProject(res);
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getProjectsByOrganizationSlug(slug: string) {
|
export async function getProjectsByOrganizationSlug(organizationSlug: string) {
|
||||||
const res = await db.project.findMany({
|
return db.project.findMany({
|
||||||
where: {
|
where: {
|
||||||
organization_slug: slug,
|
organizationSlug,
|
||||||
},
|
},
|
||||||
orderBy: {
|
orderBy: {
|
||||||
createdAt: 'desc',
|
createdAt: 'desc',
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
return res.map(transformProject);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getCurrentProjects(slug: string) {
|
export async function getCurrentProjects(organizationSlug: string) {
|
||||||
const session = auth();
|
const session = auth();
|
||||||
if (!session.userId) {
|
if (!session.userId) {
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
const access = await db.projectAccess.findMany({
|
return db.project.findMany({
|
||||||
where: {
|
where: {
|
||||||
organization_slug: slug,
|
organizationSlug,
|
||||||
user_id: session.userId,
|
},
|
||||||
|
include: {
|
||||||
|
access: true,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const res = await db.project.findMany({
|
|
||||||
where: {
|
|
||||||
organization_slug: slug,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
if (access.length === 0) {
|
|
||||||
return res.map(transformProject);
|
|
||||||
}
|
|
||||||
|
|
||||||
return res
|
|
||||||
.filter((project) => access.some((a) => a.project_id === project.id))
|
|
||||||
.map(transformProject);
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,19 +1,7 @@
|
|||||||
import type { Prisma, Reference } from '../prisma-client';
|
import type { Prisma, Reference } from '../prisma-client';
|
||||||
import { db } from '../prisma-client';
|
import { db } from '../prisma-client';
|
||||||
|
|
||||||
export type IServiceReference = Omit<Reference, 'project_id'> & {
|
export type IServiceReference = Reference;
|
||||||
projectId: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
export function transformReference({
|
|
||||||
project_id,
|
|
||||||
...item
|
|
||||||
}: Reference): IServiceReference {
|
|
||||||
return {
|
|
||||||
...item,
|
|
||||||
projectId: project_id,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function getReferenceById(id: string) {
|
export async function getReferenceById(id: string) {
|
||||||
const reference = await db.reference.findUnique({
|
const reference = await db.reference.findUnique({
|
||||||
@@ -26,7 +14,7 @@ export async function getReferenceById(id: string) {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
return transformReference(reference);
|
return reference;
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getReferences({
|
export async function getReferences({
|
||||||
@@ -38,11 +26,9 @@ export async function getReferences({
|
|||||||
take?: number;
|
take?: number;
|
||||||
skip?: number;
|
skip?: number;
|
||||||
}) {
|
}) {
|
||||||
const references = await db.reference.findMany({
|
return db.reference.findMany({
|
||||||
where,
|
where,
|
||||||
take: take ?? 50,
|
take: take ?? 50,
|
||||||
skip,
|
skip,
|
||||||
});
|
});
|
||||||
|
|
||||||
return references.map(transformReference);
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -45,11 +45,11 @@ export function transformReport(
|
|||||||
): IChartInput & { id: string } {
|
): IChartInput & { id: string } {
|
||||||
return {
|
return {
|
||||||
id: report.id,
|
id: report.id,
|
||||||
projectId: report.project_id,
|
projectId: report.projectId,
|
||||||
events: (report.events as IChartEvent[]).map(transformReportEvent),
|
events: (report.events as IChartEvent[]).map(transformReportEvent),
|
||||||
breakdowns: report.breakdowns as IChartBreakdown[],
|
breakdowns: report.breakdowns as IChartBreakdown[],
|
||||||
chartType: report.chart_type,
|
chartType: report.chartType,
|
||||||
lineType: (report.line_type as IChartLineType) ?? lineTypes.monotone,
|
lineType: (report.lineType as IChartLineType) ?? lineTypes.monotone,
|
||||||
interval: report.interval,
|
interval: report.interval,
|
||||||
name: report.name || 'Untitled',
|
name: report.name || 'Untitled',
|
||||||
range: (report.range as IChartRange) ?? timeRanges['1m'],
|
range: (report.range as IChartRange) ?? timeRanges['1m'],
|
||||||
@@ -64,7 +64,7 @@ export function getReportsByDashboardId(dashboardId: string) {
|
|||||||
return db.report
|
return db.report
|
||||||
.findMany({
|
.findMany({
|
||||||
where: {
|
where: {
|
||||||
dashboard_id: dashboardId,
|
dashboardId,
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
.then((reports) => reports.map(transformReport));
|
.then((reports) => reports.map(transformReport));
|
||||||
|
|||||||
@@ -10,3 +10,11 @@ export function getShareOverviewById(id: string) {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function getShareByProjectId(projectId: string) {
|
||||||
|
return db.shareOverview.findUnique({
|
||||||
|
where: {
|
||||||
|
projectId,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|||||||
@@ -85,7 +85,7 @@ export const zInviteUser = z.object({
|
|||||||
});
|
});
|
||||||
|
|
||||||
export const zShareOverview = z.object({
|
export const zShareOverview = z.object({
|
||||||
organizationId: z.string(),
|
organizationSlug: z.string(),
|
||||||
projectId: z.string(),
|
projectId: z.string(),
|
||||||
password: z.string().nullable(),
|
password: z.string().nullable(),
|
||||||
public: z.boolean(),
|
public: z.boolean(),
|
||||||
|
|||||||
8
pnpm-lock.yaml
generated
8
pnpm-lock.yaml
generated
@@ -301,8 +301,8 @@ importers:
|
|||||||
specifier: ^0.1.3
|
specifier: ^0.1.3
|
||||||
version: 0.1.3
|
version: 0.1.3
|
||||||
pushmodal:
|
pushmodal:
|
||||||
specifier: ^0.0.8
|
specifier: ^1.0.0
|
||||||
version: 0.0.8(@radix-ui/react-dialog@1.0.5)(react-dom@18.2.0)(react@18.2.0)
|
version: 1.0.0(@radix-ui/react-dialog@1.0.5)(react-dom@18.2.0)(react@18.2.0)
|
||||||
ramda:
|
ramda:
|
||||||
specifier: ^0.29.1
|
specifier: ^0.29.1
|
||||||
version: 0.29.1
|
version: 0.29.1
|
||||||
@@ -14709,8 +14709,8 @@ packages:
|
|||||||
resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==}
|
resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==}
|
||||||
engines: {node: '>=6'}
|
engines: {node: '>=6'}
|
||||||
|
|
||||||
/pushmodal@0.0.8(@radix-ui/react-dialog@1.0.5)(react-dom@18.2.0)(react@18.2.0):
|
/pushmodal@1.0.0(@radix-ui/react-dialog@1.0.5)(react-dom@18.2.0)(react@18.2.0):
|
||||||
resolution: {integrity: sha512-Fmsx9Fadnv51WPcXvX7x4N8BbLxMDdSStpfJ1WY4pyjqXEEht+3H51TwjZJc5PkGMURdsvi7py76SFgsmrujnw==}
|
resolution: {integrity: sha512-34JSZHJHGTcLqBgYk9Fyiw5vBYJZrcgoDE7GfHehKKzxBt/Ro2bSLTIGRnzQ+NRv389GxH6WXCBUH+6VJ1wvTg==}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
'@radix-ui/react-dialog': ^1.0.0
|
'@radix-ui/react-dialog': ^1.0.0
|
||||||
react: ^16.12.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^20.0.0 || ^21.0.0
|
react: ^16.12.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^20.0.0 || ^21.0.0
|
||||||
|
|||||||
Reference in New Issue
Block a user