chore(root): rename organizationSlug to organizationId (#91)

This commit is contained in:
Carl-Gerhard Lindesvärd
2024-11-20 14:04:55 +01:00
committed by GitHub
parent 0221948aab
commit cd16ac878d
52 changed files with 176 additions and 360 deletions

View File

@@ -183,12 +183,10 @@ async function createMock(file: string) {
await db.project.create({ await db.project.create({
data: { data: {
organizationId: 'openpanel-dev', organizationId: 'openpanel-dev',
organizationSlug: 'openpanel-dev',
name: project.domain, name: project.domain,
clients: { clients: {
create: { create: {
organizationId: 'openpanel-dev', organizationId: 'openpanel-dev',
organizationSlug: 'openpanel-dev',
name: project.domain, name: project.domain,
secret: await hashPassword('secret'), secret: await hashPassword('secret'),
id: project.clientId, id: project.clientId,

View File

@@ -38,7 +38,7 @@ async function getProjectId(
const project = await db.project.findUnique({ const project = await db.project.findUnique({
where: { where: {
organizationId: request.client?.organizationSlug, organizationId: request.client?.organizationId,
id: projectId, id: projectId,
}, },
}); });

View File

@@ -96,7 +96,6 @@ export async function clerkWebhook(
data: access data: access
.filter((a) => typeof a === 'string') .filter((a) => typeof a === 'string')
.map((projectId) => ({ .map((projectId) => ({
organizationSlug: membership.organizationId,
organizationId: membership.organizationId, organizationId: membership.organizationId,
projectId: projectId, projectId: projectId,
userId: user.id, userId: user.id,
@@ -114,7 +113,6 @@ export async function clerkWebhook(
data: access data: access
.filter((a): a is string => typeof a === 'string') .filter((a): a is string => typeof a === 'string')
.map((projectId) => ({ .map((projectId) => ({
organizationSlug: payload.data.organization.slug,
organizationId: payload.data.organization.slug, organizationId: payload.data.organization.slug,
projectId: projectId, projectId: projectId,
userId: payload.data.public_user_data.user_id, userId: payload.data.public_user_data.user_id,
@@ -152,7 +150,7 @@ export async function clerkWebhook(
if (payload.type === 'organizationMembership.deleted') { if (payload.type === 'organizationMembership.deleted') {
await db.projectAccess.deleteMany({ await db.projectAccess.deleteMany({
where: { where: {
organizationSlug: payload.data.organization.slug, organizationId: payload.data.organization.slug,
userId: payload.data.public_user_data.user_id, userId: payload.data.public_user_data.user_id,
}, },
}); });

View File

@@ -60,7 +60,7 @@ export function ListReports({ reports, dashboard }: ListReportsProps) {
icon={PlusIcon} icon={PlusIcon}
onClick={() => { onClick={() => {
router.push( router.push(
`/${params.organizationSlug}/${ `/${params.organizationId}/${
params.projectId params.projectId
}/reports?${new URLSearchParams({ }/reports?${new URLSearchParams({
dashboardId: params.dashboardId, dashboardId: params.dashboardId,
@@ -79,7 +79,7 @@ export function ListReports({ reports, dashboard }: ListReportsProps) {
return ( return (
<div className="card" key={report.id}> <div className="card" key={report.id}>
<Link <Link
href={`/${params.organizationSlug}/${params.projectId}/reports/${report.id}`} href={`/${params.organizationId}/${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
> >
@@ -163,7 +163,7 @@ export function ListReports({ reports, dashboard }: ListReportsProps) {
<Button <Button
onClick={() => onClick={() =>
router.push( router.push(
`/${params.organizationSlug}/${ `/${params.organizationId}/${
params.projectId params.projectId
}/reports?${new URLSearchParams({ }/reports?${new URLSearchParams({
dashboardId: params.dashboardId, dashboardId: params.dashboardId,

View File

@@ -7,7 +7,6 @@ import { ListReports } from './list-reports';
interface PageProps { interface PageProps {
params: { params: {
organizationSlug: string;
projectId: string; projectId: string;
dashboardId: string; dashboardId: string;
}; };

View File

@@ -37,7 +37,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 { organizationSlug, projectId } = params; const { organizationId, projectId } = params;
const deletion = api.dashboard.delete.useMutation({ const deletion = api.dashboard.delete.useMutation({
onError: (error, variables) => { onError: (error, variables) => {
return handleErrorToastOptions({ return handleErrorToastOptions({
@@ -87,7 +87,7 @@ export function ListDashboards({ dashboards }: ListDashboardsProps) {
<Card key={item.id} hover> <Card key={item.id} hover>
<div> <div>
<Link <Link
href={`/${organizationSlug}/${projectId}/dashboards/${item.id}`} href={`/${organizationId}/${projectId}/dashboards/${item.id}`}
className="flex flex-col p-4 @container" className="flex flex-col p-4 @container"
> >
<div className="col gap-2"> <div className="col gap-2">

View File

@@ -9,7 +9,6 @@ import Events from './events';
interface PageProps { interface PageProps {
params: { params: {
projectId: string; projectId: string;
organizationSlug: string;
}; };
searchParams: Record<string, string>; searchParams: Record<string, string>;
} }

View File

@@ -18,7 +18,7 @@ export default function LayoutOrganizationSelector({
const router = useRouter(); const router = useRouter();
const organization = organizations.find( const organization = organizations.find(
(item) => item.id === params.organizationSlug, (item) => item.id === params.organizationId,
); );
return ( return (

View File

@@ -14,11 +14,9 @@ import {
} from '@/components/ui/dropdown-menu'; } from '@/components/ui/dropdown-menu';
import { useAppParams } from '@/hooks/useAppParams'; import { useAppParams } from '@/hooks/useAppParams';
import { pushModal } from '@/modals'; import { pushModal } from '@/modals';
import { cn } from '@/utils/cn';
import { import {
Building2Icon, Building2Icon,
CheckIcon, CheckIcon,
ChevronsUpDown,
ChevronsUpDownIcon, ChevronsUpDownIcon,
PlusIcon, PlusIcon,
} from 'lucide-react'; } from 'lucide-react';
@@ -27,12 +25,12 @@ import { useState } from 'react';
import type { import type {
getCurrentOrganizations, getCurrentOrganizations,
getProjectsByOrganizationSlug, getProjectsByOrganizationId,
} from '@openpanel/db'; } from '@openpanel/db';
import Link from 'next/link'; import Link from 'next/link';
interface LayoutProjectSelectorProps { interface LayoutProjectSelectorProps {
projects: Awaited<ReturnType<typeof getProjectsByOrganizationSlug>>; projects: Awaited<ReturnType<typeof getProjectsByOrganizationId>>;
organizations?: Awaited<ReturnType<typeof getCurrentOrganizations>>; organizations?: Awaited<ReturnType<typeof getCurrentOrganizations>>;
align?: 'start' | 'end'; align?: 'start' | 'end';
} }
@@ -42,22 +40,22 @@ export default function LayoutProjectSelector({
align = 'start', align = 'start',
}: LayoutProjectSelectorProps) { }: LayoutProjectSelectorProps) {
const router = useRouter(); const router = useRouter();
const { organizationSlug, projectId } = useAppParams(); const { organizationId, projectId } = useAppParams();
const pathname = usePathname() || ''; const pathname = usePathname() || '';
const [open, setOpen] = useState(false); const [open, setOpen] = useState(false);
const changeProject = (newProjectId: string) => { const changeProject = (newProjectId: string) => {
if (organizationSlug && projectId) { if (organizationId && projectId) {
const split = pathname const split = pathname
.replace( .replace(
`/${organizationSlug}/${projectId}`, `/${organizationId}/${projectId}`,
`/${organizationSlug}/${newProjectId}`, `/${organizationId}/${newProjectId}`,
) )
.split('/'); .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(`/${organizationSlug}/${newProjectId}`); router.push(`/${organizationId}/${newProjectId}`);
} }
}; };
@@ -102,7 +100,7 @@ export default function LayoutProjectSelector({
))} ))}
{projects.length > 10 && ( {projects.length > 10 && (
<DropdownMenuItem asChild> <DropdownMenuItem asChild>
<Link href={`/${organizationSlug}`}>All projects</Link> <Link href={`/${organizationId}`}>All projects</Link>
</DropdownMenuItem> </DropdownMenuItem>
)} )}
<DropdownMenuItem <DropdownMenuItem
@@ -126,7 +124,7 @@ export default function LayoutProjectSelector({
onClick={() => changeOrganization(organization.id)} onClick={() => changeOrganization(organization.id)}
> >
{organization.name} {organization.name}
{organization.id === organizationSlug && ( {organization.id === organizationId && (
<DropdownMenuShortcut> <DropdownMenuShortcut>
<CheckIcon size={16} /> <CheckIcon size={16} />
</DropdownMenuShortcut> </DropdownMenuShortcut>

View File

@@ -11,7 +11,7 @@ import { useEffect, useState } from 'react';
import type { import type {
IServiceDashboards, IServiceDashboards,
IServiceOrganization, IServiceOrganization,
getProjectsByOrganizationSlug, getProjectsByOrganizationId,
} from '@openpanel/db'; } from '@openpanel/db';
import LayoutMenu from './layout-menu'; import LayoutMenu from './layout-menu';
@@ -20,9 +20,8 @@ import LayoutProjectSelector from './layout-project-selector';
interface LayoutSidebarProps { interface LayoutSidebarProps {
organizations: IServiceOrganization[]; organizations: IServiceOrganization[];
dashboards: IServiceDashboards; dashboards: IServiceDashboards;
organizationSlug: string;
projectId: string; projectId: string;
projects: Awaited<ReturnType<typeof getProjectsByOrganizationSlug>>; projects: Awaited<ReturnType<typeof getProjectsByOrganizationId>>;
} }
export function LayoutSidebar({ export function LayoutSidebar({
organizations, organizations,

View File

@@ -13,22 +13,22 @@ import SideEffects from './side-effects';
interface AppLayoutProps { interface AppLayoutProps {
children: React.ReactNode; children: React.ReactNode;
params: { params: {
organizationSlug: string; organizationId: string;
projectId: string; projectId: string;
}; };
} }
export default async function AppLayout({ export default async function AppLayout({
children, children,
params: { organizationSlug, projectId }, params: { organizationId, projectId },
}: AppLayoutProps) { }: AppLayoutProps) {
const [organizations, projects, dashboards] = await Promise.all([ const [organizations, projects, dashboards] = await Promise.all([
getCurrentOrganizations(), getCurrentOrganizations(),
getCurrentProjects(organizationSlug), getCurrentProjects(organizationId),
getDashboardsByProjectId(projectId), getDashboardsByProjectId(projectId),
]); ]);
if (!organizations.find((item) => item.id === organizationSlug)) { if (!organizations.find((item) => item.id === organizationId)) {
return ( return (
<FullPageEmptyState title="Not found" className="min-h-screen"> <FullPageEmptyState title="Not found" className="min-h-screen">
The organization you were looking for could not be found. The organization you were looking for could not be found.
@@ -48,7 +48,7 @@ export default async function AppLayout({
<div id="dashboard"> <div id="dashboard">
<LayoutSidebar <LayoutSidebar
{...{ {...{
organizationSlug, organizationId,
projectId, projectId,
organizations, organizations,
projects, projects,

View File

@@ -13,7 +13,6 @@ import { OverviewReportRange } from './overview-sticky-header';
interface PageProps { interface PageProps {
params: { params: {
organizationSlug: string;
projectId: string; projectId: string;
}; };
} }

View File

@@ -1,5 +1,4 @@
import ClickToCopy from '@/components/click-to-copy'; import ClickToCopy from '@/components/click-to-copy';
import { ListPropertiesIcon } from '@/components/events/list-properties-icon';
import { ProfileAvatar } from '@/components/profiles/profile-avatar'; import { ProfileAvatar } from '@/components/profiles/profile-avatar';
import { Padding } from '@/components/ui/padding'; import { Padding } from '@/components/ui/padding';
import { getProfileName } from '@/utils/getters'; import { getProfileName } from '@/utils/getters';
@@ -16,7 +15,6 @@ import ProfileMetrics from './profile-metrics';
interface PageProps { interface PageProps {
params: { params: {
organizationSlug: string;
projectId: string; projectId: string;
profileId: string; profileId: string;
}; };

View File

@@ -8,7 +8,6 @@ import Profiles from './profiles';
interface PageProps { interface PageProps {
params: { params: {
projectId: string; projectId: string;
organizationSlug: string;
}; };
searchParams: Record<string, string>; searchParams: Record<string, string>;
} }

View File

@@ -13,7 +13,6 @@ import RealtimeReloader from './realtime-reloader';
type Props = { type Props = {
params: { params: {
organizationSlug: string;
projectId: string; projectId: string;
}; };
}; };

View File

@@ -35,12 +35,12 @@ interface Props {
export default function CreateInvite({ projects }: Props) { export default function CreateInvite({ projects }: Props) {
const router = useRouter(); const router = useRouter();
const { organizationSlug } = useAppParams(); const { organizationId } = useAppParams();
const { register, handleSubmit, formState, reset, control } = useForm<IForm>({ const { register, handleSubmit, formState, reset, control } = useForm<IForm>({
resolver: zodResolver(zInviteUser), resolver: zodResolver(zInviteUser),
defaultValues: { defaultValues: {
organizationSlug, organizationId,
access: [], access: [],
role: 'org:member', role: 'org:member',
}, },

View File

@@ -1,18 +1,18 @@
import { TableButtons } from '@/components/data-table'; import { TableButtons } from '@/components/data-table';
import { InvitesTable } from '@/components/settings/invites'; import { InvitesTable } from '@/components/settings/invites';
import { getInvites, getProjectsByOrganizationSlug } from '@openpanel/db'; import { getInvites, getProjectsByOrganizationId } from '@openpanel/db';
import CreateInvite from './create-invite'; import CreateInvite from './create-invite';
interface Props { interface Props {
organizationSlug: string; organizationId: string;
} }
const InvitesServer = async ({ organizationSlug }: Props) => { const InvitesServer = async ({ organizationId }: Props) => {
const [invites, projects] = await Promise.all([ const [invites, projects] = await Promise.all([
getInvites(organizationSlug), getInvites(organizationId),
getProjectsByOrganizationSlug(organizationSlug), getProjectsByOrganizationId(organizationId),
]); ]);
return ( return (

View File

@@ -1,15 +1,15 @@
import { MembersTable } from '@/components/settings/members'; import { MembersTable } from '@/components/settings/members';
import { getMembers, getProjectsByOrganizationSlug } from '@openpanel/db'; import { getMembers, getProjectsByOrganizationId } from '@openpanel/db';
interface Props { interface Props {
organizationSlug: string; organizationId: string;
} }
const MembersServer = async ({ organizationSlug }: Props) => { const MembersServer = async ({ organizationId }: Props) => {
const [members, projects] = await Promise.all([ const [members, projects] = await Promise.all([
getMembers(organizationSlug), getMembers(organizationId),
getProjectsByOrganizationSlug(organizationSlug), getProjectsByOrganizationId(organizationId),
]); ]);
return <MembersTable data={members} projects={projects} />; return <MembersTable data={members} projects={projects} />;

View File

@@ -14,13 +14,13 @@ import MembersServer from './members';
interface PageProps { interface PageProps {
params: { params: {
organizationSlug: string; organizationId: string;
}; };
searchParams: Record<string, string>; searchParams: Record<string, string>;
} }
export default async function Page({ export default async function Page({
params: { organizationSlug }, params: { organizationId },
searchParams, searchParams,
}: PageProps) { }: PageProps) {
const tab = parseAsStringEnum(['org', 'members', 'invites']) const tab = parseAsStringEnum(['org', 'members', 'invites'])
@@ -29,7 +29,7 @@ export default async function Page({
const session = auth(); const session = auth();
const organization = await db.organization.findUnique({ const organization = await db.organization.findUnique({
where: { where: {
id: organizationSlug, id: organizationId,
members: { members: {
some: { some: {
userId: session.userId, userId: session.userId,
@@ -80,12 +80,8 @@ export default async function Page({
</PageTabs> </PageTabs>
{tab === 'org' && <EditOrganization organization={organization} />} {tab === 'org' && <EditOrganization organization={organization} />}
{tab === 'members' && ( {tab === 'members' && <MembersServer organizationId={organizationId} />}
<MembersServer organizationSlug={organizationSlug} /> {tab === 'invites' && <InvitesServer organizationId={organizationId} />}
)}
{tab === 'invites' && (
<InvitesServer organizationSlug={organizationSlug} />
)}
</Padding> </Padding>
); );
} }

View File

@@ -1,25 +1,22 @@
import PageLayout from '@/app/(app)/[organizationSlug]/[projectId]/page-layout';
import { Padding } from '@/components/ui/padding'; import { Padding } from '@/components/ui/padding';
import { import {
getClientsByOrganizationSlug, getClientsByOrganizationId,
getProjectsByOrganizationSlug, getProjectsByOrganizationId,
} from '@openpanel/db'; } from '@openpanel/db';
import ListProjects from './list-projects'; import ListProjects from './list-projects';
interface PageProps { interface PageProps {
params: { params: {
organizationSlug: string; organizationId: string;
}; };
} }
export default async function Page({ export default async function Page({ params: { organizationId } }: PageProps) {
params: { organizationSlug },
}: PageProps) {
const [projects, clients] = await Promise.all([ const [projects, clients] = await Promise.all([
getProjectsByOrganizationSlug(organizationSlug), getProjectsByOrganizationId(organizationId),
getClientsByOrganizationSlug(organizationSlug), getClientsByOrganizationId(organizationId),
]); ]);
return ( return (

View File

@@ -4,7 +4,6 @@ import ListReferences from './list-references';
interface PageProps { interface PageProps {
params: { params: {
organizationSlug: string;
projectId: string; projectId: string;
}; };
} }

View File

@@ -8,19 +8,17 @@ import { getCurrentOrganizations, getCurrentProjects } from '@openpanel/db';
interface PageProps { interface PageProps {
params: { params: {
organizationSlug: string; organizationId: string;
}; };
} }
export default async function Page({ export default async function Page({ params: { organizationId } }: PageProps) {
params: { organizationSlug },
}: PageProps) {
const [organizations, projects] = await Promise.all([ const [organizations, projects] = await Promise.all([
getCurrentOrganizations(), getCurrentOrganizations(),
getCurrentProjects(organizationSlug), getCurrentProjects(organizationId),
]); ]);
const organization = organizations.find((org) => org.id === organizationSlug); const organization = organizations.find((org) => org.id === organizationId);
if (!organization) { if (!organization) {
return ( return (
@@ -35,7 +33,7 @@ export default async function Page({
} }
if (projects.length === 1 && projects[0]) { if (projects.length === 1 && projects[0]) {
return redirect(`/${organizationSlug}/${projects[0].id}`); return redirect(`/${organizationId}/${projects[0].id}`);
} }
return ( return (

View File

@@ -10,8 +10,8 @@ type Props = {
const Connect = async ({ params: { projectId } }: Props) => { const Connect = async ({ params: { projectId } }: Props) => {
const orgs = await getCurrentOrganizations(); const orgs = await getCurrentOrganizations();
const organizationSlug = orgs[0]?.id; const organizationId = orgs[0]?.id;
if (!organizationSlug) { if (!organizationId) {
throw new Error('No organization found'); throw new Error('No organization found');
} }
const project = await getProjectWithClients(projectId); const project = await getProjectWithClients(projectId);

View File

@@ -72,7 +72,7 @@ const Verify = ({ project, events }: Props) => {
<div className="flex items-center gap-8"> <div className="flex items-center gap-8">
{!verified && ( {!verified && (
<Link <Link
href={`/${project.organizationSlug}/${project.id}`} href={`/${project.organizationId}/${project.id}`}
className=" text-muted-foreground underline" className=" text-muted-foreground underline"
> >
Skip for now Skip for now

View File

@@ -18,8 +18,8 @@ type Props = {
const Verify = async ({ params: { projectId } }: Props) => { const Verify = async ({ params: { projectId } }: Props) => {
const orgs = await getCurrentOrganizations(); const orgs = await getCurrentOrganizations();
const organizationSlug = orgs[0]?.id; const organizationId = orgs[0]?.id;
if (!organizationSlug) { if (!organizationId) {
throw new Error('No organization found'); throw new Error('No organization found');
} }
const [project, events] = await Promise.all([ const [project, events] = await Promise.all([

View File

@@ -100,7 +100,7 @@ const Tracking = ({
{organizations.length > 0 ? ( {organizations.length > 0 ? (
<Controller <Controller
control={form.control} control={form.control}
name="organizationSlug" name="organizationId"
render={({ field, formState }) => { render={({ field, formState }) => {
return ( return (
<div> <div>
@@ -109,7 +109,7 @@ const Tracking = ({
className="w-full" className="w-full"
placeholder="Select workspace" placeholder="Select workspace"
icon={Building} icon={Building}
error={formState.errors.organizationSlug?.message} error={formState.errors.organizationId?.message}
value={field.value} value={field.value}
items={ items={
organizations organizations

View File

@@ -1,9 +1,7 @@
import { StickyBelowHeader } from '@/app/(app)/[organizationSlug]/[projectId]/layout-sticky-below-header'; import { StickyBelowHeader } from '@/app/(app)/[organizationSlug]/[projectId]/layout-sticky-below-header';
import { OverviewReportRange } from '@/app/(app)/[organizationSlug]/[projectId]/overview-sticky-header'; import { OverviewReportRange } from '@/app/(app)/[organizationSlug]/[projectId]/overview-sticky-header';
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';
import { OverviewLiveHistogram } from '@/components/overview/overview-live-histogram';
import OverviewMetrics from '@/components/overview/overview-metrics'; import OverviewMetrics from '@/components/overview/overview-metrics';
import OverviewTopDevices from '@/components/overview/overview-top-devices'; import OverviewTopDevices from '@/components/overview/overview-top-devices';
import OverviewTopEvents from '@/components/overview/overview-top-events'; import OverviewTopEvents from '@/components/overview/overview-top-events';
@@ -35,7 +33,7 @@ export default async function Page({
return notFound(); return notFound();
} }
const projectId = share.projectId; const projectId = share.projectId;
const organization = await getOrganizationBySlug(share.organizationSlug); const organization = await getOrganizationBySlug(share.organizationId);
return ( return (
<div> <div>

View File

@@ -54,7 +54,6 @@ export async function POST(request: Request) {
data: access data: access
.filter((a) => typeof a === 'string') .filter((a) => typeof a === 'string')
.map((projectId) => ({ .map((projectId) => ({
organizationSlug: membership.organizationId,
organizationId: membership.organizationId, organizationId: membership.organizationId,
projectId: projectId, projectId: projectId,
userId: user.id, userId: user.id,
@@ -72,7 +71,6 @@ 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) => ({
organizationSlug: payload.data.organization.slug,
organizationId: payload.data.organization.slug, organizationId: payload.data.organization.slug,
projectId: projectId, projectId: projectId,
userId: payload.data.public_user_data.user_id, userId: payload.data.public_user_data.user_id,
@@ -110,7 +108,7 @@ 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: {
organizationSlug: payload.data.organization.slug, organizationId: payload.data.organization.slug,
userId: payload.data.public_user_data.user_id, userId: payload.data.public_user_data.user_id,
}, },
}); });

View File

@@ -16,7 +16,7 @@ import { EventIcon } from './event-icon';
type EventListItemProps = IServiceEventMinimal | IServiceEvent; type EventListItemProps = IServiceEventMinimal | IServiceEvent;
export function EventListItem(props: EventListItemProps) { export function EventListItem(props: EventListItemProps) {
const { organizationSlug, projectId } = useAppParams(); const { organizationId, projectId } = useAppParams();
const { createdAt, name, path, duration, meta } = props; const { createdAt, name, path, duration, meta } = props;
const profile = 'profile' in props ? props.profile : null; const profile = 'profile' in props ? props.profile : null;
@@ -88,7 +88,7 @@ export function EventListItem(props: EventListItemProps) {
onClick={(e) => { onClick={(e) => {
e.stopPropagation(); e.stopPropagation();
}} }}
href={`/${organizationSlug}/${projectId}/profiles/${profile?.id}`} href={`/${organizationId}/${projectId}/profiles/${profile?.id}`}
className="max-w-[80px] overflow-hidden text-ellipsis whitespace-nowrap text-muted-foreground hover:underline" className="max-w-[80px] overflow-hidden text-ellipsis whitespace-nowrap text-muted-foreground hover:underline"
> >
{getProfileName(profile)} {getProfileName(profile)}

View File

@@ -10,12 +10,12 @@ export function ProjectLink({
className?: string; className?: string;
title?: string; title?: string;
}) { }) {
const { organizationSlug, projectId } = useAppParams(); const { organizationId, projectId } = useAppParams();
if (typeof props.href === 'string') { if (typeof props.href === 'string') {
return ( return (
<Link <Link
{...props} {...props}
href={`/${organizationSlug}/${projectId}/${props.href.replace( href={`/${organizationId}/${projectId}/${props.href.replace(
/^\//, /^\//,
'', '',
)}`} )}`}

View File

@@ -8,13 +8,13 @@ import { TABLE_NAMES, chQuery } from '@openpanel/db';
import { ChartSSR } from '../chart-ssr'; import { ChartSSR } from '../chart-ssr';
import { FadeIn } from '../fade-in'; import { FadeIn } from '../fade-in';
function ProjectCard({ id, name, organizationSlug }: IServiceProject) { function ProjectCard({ id, name, organizationId }: IServiceProject) {
// For some unknown reason I get when navigating back to this page when using <Link /> // For some unknown reason I get when navigating back to this page when using <Link />
// Should be solved: https://github.com/vercel/next.js/issues/61336 // Should be solved: https://github.com/vercel/next.js/issues/61336
// But still get the error // But still get the error
return ( return (
<a <a
href={`/${organizationSlug}/${id}`} href={`/${organizationId}/${id}`}
className="card inline-flex flex-col gap-2 p-4 transition-transform hover:-translate-y-1" className="card inline-flex flex-col gap-2 p-4 transition-transform hover:-translate-y-1"
> >
<div className="font-medium">{name}</div> <div className="font-medium">{name}</div>

View File

@@ -96,7 +96,7 @@ function AccessCell({
setAccess(newAccess); setAccess(newAccess);
mutation.mutate({ mutation.mutate({
userId: row.original.user!.id, userId: row.original.user!.id,
organizationSlug: row.original.organizationId, organizationId: row.original.organizationId,
access: newAccess as string[], access: newAccess as string[],
}); });
}} }}

View File

@@ -1,16 +1,14 @@
import { useParams } from 'next/navigation'; import { useParams } from 'next/navigation';
type AppParams = { type AppParams = {
organizationSlug: string;
organizationId: string; organizationId: string;
projectId: string; projectId: string;
}; };
export function useAppParams<T>() { export function useAppParams<T>() {
const params = useParams<T & AppParams>(); const params = useParams<T & AppParams & { organizationSlug: string }>();
return { return {
...(params ?? {}), ...(params ?? {}),
organizationSlug: params?.organizationSlug,
organizationId: params?.organizationSlug, organizationId: params?.organizationSlug,
projectId: params?.projectId, projectId: params?.projectId,
} as T & AppParams; } as T & AppParams;

View File

@@ -39,7 +39,7 @@ interface Props {
projectId: string; projectId: string;
} }
export default function AddClient(props: Props) { export default function AddClient(props: Props) {
const { organizationSlug, projectId } = useAppParams(); const { organizationId, projectId } = useAppParams();
const router = useRouter(); const router = useRouter();
const form = useForm<IForm>({ const form = useForm<IForm>({
resolver: zodResolver(validation), resolver: zodResolver(validation),
@@ -60,7 +60,7 @@ export default function AddClient(props: Props) {
}, },
}); });
const query = api.project.list.useQuery({ const query = api.project.list.useQuery({
organizationSlug, organizationId,
}); });
const onSubmit: SubmitHandler<IForm> = (values) => { const onSubmit: SubmitHandler<IForm> = (values) => {
if (hasDomain && values.cors === '') { if (hasDomain && values.cors === '') {
@@ -73,7 +73,7 @@ export default function AddClient(props: Props) {
name: values.name, name: values.name,
cors: hasDomain ? values.cors : null, cors: hasDomain ? values.cors : null,
projectId: values.projectId, projectId: values.projectId,
organizationSlug, organizationId,
type: values.type, type: values.type,
crossDomain: values.crossDomain, crossDomain: values.crossDomain,
}); });

View File

@@ -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, organizationSlug } = useAppParams(); const { projectId, organizationId } = useAppParams();
const router = useRouter(); const router = useRouter();
const { register, handleSubmit, formState } = useForm<IForm>({ const { register, handleSubmit, formState } = useForm<IForm>({
@@ -32,7 +32,7 @@ export default function AddDashboard() {
const mutation = api.dashboard.create.useMutation({ const mutation = api.dashboard.create.useMutation({
onError: handleError, onError: handleError,
onSuccess(res) { onSuccess(res) {
router.push(`/${organizationSlug}/${projectId}/dashboards/${res.id}`); router.push(`/${organizationId}/${projectId}/dashboards/${res.id}`);
toast('Success', { toast('Success', {
description: 'Dashboard created.', description: 'Dashboard created.',
}); });

View File

@@ -19,7 +19,7 @@ const validator = z.object({
type IForm = z.infer<typeof validator>; type IForm = z.infer<typeof validator>;
export default function AddProject() { export default function AddProject() {
const { organizationSlug } = useAppParams(); const { organizationId } = useAppParams();
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() {
onSubmit={handleSubmit((values) => { onSubmit={handleSubmit((values) => {
mutation.mutate({ mutation.mutate({
...values, ...values,
organizationSlug, organizationId,
}); });
})} })}
> >

View File

@@ -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 { organizationSlug, projectId } = useAppParams(); const { organizationId, projectId } = useAppParams();
const searchParams = useSearchParams(); const searchParams = useSearchParams();
const dashboardId = searchParams?.get('dashboardId') ?? undefined; const dashboardId = searchParams?.get('dashboardId') ?? undefined;
@@ -42,7 +42,7 @@ export default function SaveReport({ report }: SaveReportProps) {
}); });
popModal(); popModal();
router.push( router.push(
`/${organizationSlug}/${projectId}/reports/${ `/${organizationId}/${projectId}/reports/${
res.id res.id
}?${searchParams?.toString()}`, }?${searchParams?.toString()}`,
); );

View File

@@ -20,7 +20,7 @@ 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, organizationSlug } = useAppParams(); const { projectId, organizationId } = useAppParams();
const router = useRouter(); const router = useRouter();
const { register, handleSubmit, control } = useForm<IForm>({ const { register, handleSubmit, control } = useForm<IForm>({
@@ -29,7 +29,7 @@ export default function ShareOverviewModal() {
public: true, public: true,
password: '', password: '',
projectId, projectId,
organizationSlug, organizationId,
}, },
}); });

View File

@@ -74,13 +74,12 @@ model Member {
} }
model Project { model Project {
id String @id @default(dbgenerated("gen_random_uuid()")) id String @id @default(dbgenerated("gen_random_uuid()"))
name String name String
organizationSlug String organization Organization @relation(fields: [organizationId], references: [id])
organization Organization? @relation(fields: [organizationId], references: [id]) organizationId String
organizationId String? eventsCount Int @default(0)
eventsCount Int @default(0) types ProjectType[] @default([])
types ProjectType[] @default([])
events Event[] events Event[]
profiles Profile[] profiles Profile[]
@@ -108,17 +107,16 @@ 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
projectId String projectId String
project Project @relation(fields: [projectId], references: [id]) project Project @relation(fields: [projectId], references: [id])
organizationSlug String organization Organization @relation(fields: [organizationId], references: [id])
organization Organization? @relation(fields: [organizationId], references: [id]) organizationId String
organizationId String? userId String
userId String user User @relation(fields: [userId], references: [id])
user User @relation(fields: [userId], references: [id]) level AccessLevel
level AccessLevel createdAt DateTime @default(now())
createdAt DateTime @default(now()) updatedAt DateTime @default(now()) @updatedAt
updatedAt DateTime @default(now()) @updatedAt
@@map("project_access") @@map("project_access")
} }
@@ -169,17 +167,16 @@ enum ClientType {
} }
model Client { 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?
type ClientType @default(write) type ClientType @default(write)
projectId String? projectId String?
project Project? @relation(fields: [projectId], references: [id]) project Project? @relation(fields: [projectId], references: [id])
organizationSlug String organization Organization @relation(fields: [organizationId], references: [id])
organization Organization? @relation(fields: [organizationId], references: [id]) organizationId String
organizationId String? cors String?
cors String? crossDomain Boolean @default(false)
crossDomain Boolean @default(false)
createdAt DateTime @default(now()) createdAt DateTime @default(now())
updatedAt DateTime @default(now()) @updatedAt updatedAt DateTime @default(now()) @updatedAt
@@ -208,14 +205,13 @@ 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
organizationSlug String organization Organization @relation(fields: [organizationId], references: [id])
organization Organization? @relation(fields: [organizationId], references: [id]) organizationId String
organizationId String? projectId String
projectId String project Project @relation(fields: [projectId], references: [id])
project Project @relation(fields: [projectId], references: [id]) reports Report[]
reports Report[]
createdAt DateTime @default(now()) createdAt DateTime @default(now())
updatedAt DateTime @default(now()) @updatedAt updatedAt DateTime @default(now()) @updatedAt
@@ -269,16 +265,15 @@ model Waitlist {
} }
model ShareOverview { model ShareOverview {
id String @unique id String @unique
projectId String @unique projectId String @unique
project Project @relation(fields: [projectId], references: [id]) project Project @relation(fields: [projectId], references: [id])
organizationSlug String organization Organization @relation(fields: [organizationId], references: [id])
organization Organization? @relation(fields: [organizationId], references: [id]) organizationId String
organizationId String? public Boolean @default(false)
public Boolean @default(false) password String?
password String? createdAt DateTime @default(now())
createdAt DateTime @default(now()) updatedAt DateTime @default(now()) @updatedAt
updatedAt DateTime @default(now()) @updatedAt
@@map("shares") @@map("shares")
} }

View File

@@ -8,10 +8,10 @@ export type IServiceClientWithProject = Prisma.ClientGetPayload<{
}; };
}>; }>;
export async function getClientsByOrganizationSlug(organizationSlug: string) { export async function getClientsByOrganizationId(organizationId: string) {
return db.client.findMany({ return db.client.findMany({
where: { where: {
organizationSlug, organizationId,
}, },
include: { include: {
project: true, project: true,

View File

@@ -66,10 +66,10 @@ export async function getOrganizationByProjectId(projectId: string) {
return transformOrganization(project.organization); return transformOrganization(project.organization);
} }
export async function getInvites(organizationSlug: string) { export async function getInvites(organizationId: string) {
return db.member.findMany({ return db.member.findMany({
where: { where: {
organizationId: organizationSlug, organizationId,
userId: null, userId: null,
}, },
include: { include: {
@@ -78,11 +78,11 @@ export async function getInvites(organizationSlug: string) {
}); });
} }
export async function getMembers(organizationSlug: string) { export async function getMembers(organizationId: string) {
const [members, access] = await Promise.all([ const [members, access] = await Promise.all([
db.member.findMany({ db.member.findMany({
where: { where: {
organizationId: organizationSlug, organizationId,
userId: { userId: {
not: null, not: null,
}, },
@@ -93,7 +93,7 @@ export async function getMembers(organizationSlug: string) {
}), }),
db.projectAccess.findMany({ db.projectAccess.findMany({
where: { where: {
organizationSlug, organizationId,
}, },
}), }),
]); ]);
@@ -104,10 +104,10 @@ export async function getMembers(organizationSlug: string) {
})); }));
} }
export async function getMember(organizationSlug: string, userId: string) { export async function getMember(organizationId: string, userId: string) {
return db.member.findFirst({ return db.member.findFirst({
where: { where: {
organizationId: organizationSlug, organizationId,
userId, userId,
}, },
}); });

View File

@@ -44,10 +44,10 @@ export async function getProjectWithClients(id: string) {
return res; return res;
} }
export async function getProjectsByOrganizationSlug(organizationSlug: string) { export async function getProjectsByOrganizationId(organizationId: string) {
return db.project.findMany({ return db.project.findMany({
where: { where: {
organizationSlug, organizationId,
}, },
orderBy: { orderBy: {
createdAt: 'desc', createdAt: 'desc',
@@ -55,7 +55,7 @@ export async function getProjectsByOrganizationSlug(organizationSlug: string) {
}); });
} }
export async function getCurrentProjects(organizationSlug: string) { export async function getCurrentProjects(organizationId: string) {
const session = auth(); const session = auth();
if (!session.userId) { if (!session.userId) {
return []; return [];
@@ -64,7 +64,7 @@ export async function getCurrentProjects(organizationSlug: string) {
const [projects, members, access] = await Promise.all([ const [projects, members, access] = await Promise.all([
db.project.findMany({ db.project.findMany({
where: { where: {
organizationSlug, organizationId,
}, },
orderBy: { orderBy: {
eventsCount: 'desc', eventsCount: 'desc',
@@ -73,13 +73,13 @@ export async function getCurrentProjects(organizationSlug: string) {
db.member.findMany({ db.member.findMany({
where: { where: {
userId: session.userId, userId: session.userId,
organizationId: organizationSlug, organizationId,
}, },
}), }),
db.projectAccess.findMany({ db.projectAccess.findMany({
where: { where: {
userId: session.userId, userId: session.userId,
organizationId: organizationSlug, organizationId,
}, },
}), }),
]); ]);

View File

@@ -12,7 +12,7 @@ export async function getProjectAccess({
try { try {
// Check if user has access to the project // Check if user has access to the project
const project = await getProjectById(projectId); const project = await getProjectById(projectId);
if (!project?.organizationSlug) { if (!project?.organizationId) {
return false; return false;
} }
@@ -20,12 +20,12 @@ export async function getProjectAccess({
db.projectAccess.findMany({ db.projectAccess.findMany({
where: { where: {
userId, userId,
organizationId: project.organizationSlug, organizationId: project.organizationId,
}, },
}), }),
db.member.findFirst({ db.member.findFirst({
where: { where: {
organizationId: project.organizationSlug, organizationId: project.organizationId,
userId, userId,
}, },
}), }),

View File

@@ -46,7 +46,7 @@ export const clientRouter = createTRPCRouter({
z.object({ z.object({
name: z.string(), name: z.string(),
projectId: z.string(), projectId: z.string(),
organizationSlug: z.string(), organizationId: z.string(),
cors: z.string().nullable(), cors: z.string().nullable(),
crossDomain: z.boolean().optional(), crossDomain: z.boolean().optional(),
type: z.enum(['read', 'write', 'root']).optional(), type: z.enum(['read', 'write', 'root']).optional(),
@@ -55,8 +55,7 @@ export const clientRouter = createTRPCRouter({
.mutation(async ({ input }) => { .mutation(async ({ input }) => {
const secret = `sec_${crypto.randomBytes(10).toString('hex')}`; const secret = `sec_${crypto.randomBytes(10).toString('hex')}`;
const data: Prisma.ClientCreateArgs['data'] = { const data: Prisma.ClientCreateArgs['data'] = {
organizationSlug: input.organizationSlug, organizationId: input.organizationId,
organizationId: input.organizationSlug,
projectId: input.projectId, projectId: input.projectId,
name: input.name, name: input.name,
type: input.type ?? 'write', type: input.type ?? 'write',

View File

@@ -50,7 +50,6 @@ export const dashboardRouter = createTRPCRouter({
data: { data: {
id: await getId('dashboard', input.name), id: await getId('dashboard', input.name),
projectId: input.projectId, projectId: input.projectId,
organizationSlug: project.organizationId!,
organizationId: project.organizationId, organizationId: project.organizationId,
name: input.name, name: input.name,
}, },

View File

@@ -13,8 +13,8 @@ async function createOrGetOrganization(
input: z.infer<typeof zOnboardingProject>, input: z.infer<typeof zOnboardingProject>,
userId: string, userId: string,
) { ) {
if (input.organizationSlug) { if (input.organizationId) {
return await getOrganizationBySlug(input.organizationSlug); return await getOrganizationBySlug(input.organizationId);
} }
if (input.organization) { if (input.organization) {
@@ -92,7 +92,6 @@ export const onboardingRouter = createTRPCRouter({
data: { data: {
id: await getId('project', input.project), id: await getId('project', input.project),
name: input.project, name: input.project,
organizationSlug: organization.id,
organizationId: organization.id, organizationId: organization.id,
types, types,
}, },
@@ -102,7 +101,6 @@ 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`,
organizationSlug: organization.id,
organizationId: organization.id, organizationId: organization.id,
projectId: project.id, projectId: project.id,
type: 'write', type: 'write',

View File

@@ -42,7 +42,7 @@ export const organizationRouter = createTRPCRouter({
.mutation(async ({ input, ctx }) => { .mutation(async ({ input, ctx }) => {
const access = await getOrganizationAccess({ const access = await getOrganizationAccess({
userId: ctx.session.userId, userId: ctx.session.userId,
organizationId: input.organizationSlug, organizationId: input.organizationId,
}); });
if (access?.role !== 'org:admin') { if (access?.role !== 'org:admin') {
@@ -72,7 +72,7 @@ export const organizationRouter = createTRPCRouter({
return db.member.create({ return db.member.create({
data: { data: {
email, email,
organizationId: input.organizationSlug, organizationId: input.organizationId,
role: input.role, role: input.role,
invitedById: ctx.session.userId, invitedById: ctx.session.userId,
meta: { meta: {
@@ -175,7 +175,7 @@ export const organizationRouter = createTRPCRouter({
.input( .input(
z.object({ z.object({
userId: z.string(), userId: z.string(),
organizationSlug: z.string(), organizationId: z.string(),
access: z.array(z.string()), access: z.array(z.string()),
}), }),
) )
@@ -186,7 +186,7 @@ export const organizationRouter = createTRPCRouter({
const access = await getOrganizationAccess({ const access = await getOrganizationAccess({
userId: ctx.session.userId, userId: ctx.session.userId,
organizationId: input.organizationSlug, organizationId: input.organizationId,
}); });
if (access?.role !== 'org:admin') { if (access?.role !== 'org:admin') {
@@ -197,14 +197,13 @@ export const organizationRouter = createTRPCRouter({
db.projectAccess.deleteMany({ db.projectAccess.deleteMany({
where: { where: {
userId: input.userId, userId: input.userId,
organizationId: input.organizationSlug, organizationId: input.organizationId,
}, },
}), }),
db.projectAccess.createMany({ db.projectAccess.createMany({
data: input.access.map((projectId) => ({ data: input.access.map((projectId) => ({
userId: input.userId, userId: input.userId,
organizationSlug: input.organizationSlug, organizationId: input.organizationId,
organizationId: input.organizationSlug,
projectId: projectId, projectId: projectId,
level: 'read', level: 'read',
})), })),

View File

@@ -4,7 +4,7 @@ import {
db, db,
getId, getId,
getProjectByIdCached, getProjectByIdCached,
getProjectsByOrganizationSlug, getProjectsByOrganizationId,
} from '@openpanel/db'; } from '@openpanel/db';
import { getProjectAccess } from '../access'; import { getProjectAccess } from '../access';
@@ -15,12 +15,12 @@ export const projectRouter = createTRPCRouter({
list: protectedProcedure list: protectedProcedure
.input( .input(
z.object({ z.object({
organizationSlug: z.string().nullable(), organizationId: z.string().nullable(),
}), }),
) )
.query(async ({ input: { organizationSlug } }) => { .query(async ({ input: { organizationId } }) => {
if (organizationSlug === null) return []; if (organizationId === null) return [];
return getProjectsByOrganizationSlug(organizationSlug); return getProjectsByOrganizationId(organizationId);
}), }),
update: protectedProcedure update: protectedProcedure
@@ -54,15 +54,14 @@ export const projectRouter = createTRPCRouter({
.input( .input(
z.object({ z.object({
name: z.string().min(1), name: z.string().min(1),
organizationSlug: z.string(), organizationId: z.string(),
}), }),
) )
.mutation(async ({ input: { name, organizationSlug } }) => { .mutation(async ({ input: { name, organizationId } }) => {
return db.project.create({ return db.project.create({
data: { data: {
id: await getId('project', name), id: await getId('project', name),
organizationSlug: organizationSlug, organizationId,
organizationId: organizationSlug,
name: name, name: name,
}, },
}); });

View File

@@ -17,8 +17,7 @@ export const shareRouter = createTRPCRouter({
}, },
create: { create: {
id: uid.rnd(), id: uid.rnd(),
organizationSlug: input.organizationSlug, organizationId: input.organizationId,
organizationId: input.organizationSlug,
projectId: input.projectId, projectId: input.projectId,
public: input.public, public: input.public,
password: input.password || null, password: input.password || null,

View File

@@ -88,17 +88,6 @@ const enforceAccess = t.middleware(async ({ ctx, next, rawInput }) => {
} }
} }
if (has('organizationSlug', rawInput)) {
const access = await getOrganizationAccessCached({
userId: ctx.session.userId!,
organizationId: rawInput.organizationSlug as string,
});
if (!access) {
throw TRPCAccessError('You do not have access to this organization');
}
}
return next(); return next();
}); });

View File

@@ -88,13 +88,13 @@ export const zReportInput = zChartInput.extend({
export const zInviteUser = z.object({ export const zInviteUser = z.object({
email: z.string().email(), email: z.string().email(),
organizationSlug: z.string(), organizationId: z.string(),
role: z.enum(['org:admin', 'org:member']), role: z.enum(['org:admin', 'org:member']),
access: z.array(z.string()), access: z.array(z.string()),
}); });
export const zShareOverview = z.object({ export const zShareOverview = z.object({
organizationSlug: z.string(), organizationId: z.string(),
projectId: z.string(), projectId: z.string(),
password: z.string().nullable(), password: z.string().nullable(),
public: z.boolean(), public: z.boolean(),
@@ -110,7 +110,7 @@ export const zCreateReference = z.object({
export const zOnboardingProject = z export const zOnboardingProject = z
.object({ .object({
organization: z.string().optional(), organization: z.string().optional(),
organizationSlug: z.string().optional(), organizationId: z.string().optional(),
project: z.string().min(3), project: z.string().min(3),
domain: z.string().url().or(z.literal('').or(z.null())), domain: z.string().url().or(z.literal('').or(z.null())),
website: z.boolean(), website: z.boolean(),
@@ -118,7 +118,7 @@ export const zOnboardingProject = z
backend: z.boolean(), backend: z.boolean(),
}) })
.superRefine((data, ctx) => { .superRefine((data, ctx) => {
if (!data.organization && !data.organizationSlug) { if (!data.organization && !data.organizationId) {
ctx.addIssue({ ctx.addIssue({
code: 'custom', code: 'custom',
message: 'Organization is required', message: 'Organization is required',
@@ -127,7 +127,7 @@ export const zOnboardingProject = z
ctx.addIssue({ ctx.addIssue({
code: 'custom', code: 'custom',
message: 'Organization is required', message: 'Organization is required',
path: ['organizationSlug'], path: ['organizationId'],
}); });
} }

155
pnpm-lock.yaml generated
View File

@@ -1013,14 +1013,14 @@ importers:
specifier: 1.0.1-local specifier: 1.0.1-local
version: link:../web version: link:../web
next: next:
specifier: ^12.0.0 || ^13.0.0 || ^14.0.0 specifier: ^12.0.0 || ^13.0.0 || ^14.0.0 || ^15.0.0
version: 14.1.0(@opentelemetry/api@1.8.0)(react-dom@18.2.0)(react@18.2.0) version: 15.0.3(@opentelemetry/api@1.8.0)(react-dom@18.3.1)(react@18.3.1)
react: react:
specifier: ^16.8.0 || ^17.0.0 || ^18.0.0 specifier: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0
version: 18.2.0 version: 18.3.1
react-dom: react-dom:
specifier: ^16.8.0 || ^17.0.0 || ^18.0.0 specifier: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0
version: 18.2.0(react@18.2.0) version: 18.3.1(react@18.3.1)
devDependencies: devDependencies:
'@openpanel/tsconfig': '@openpanel/tsconfig':
specifier: workspace:* specifier: workspace:*
@@ -3809,7 +3809,7 @@ packages:
getenv: 1.0.0 getenv: 1.0.0
glob: 7.1.6 glob: 7.1.6
resolve-from: 5.0.0 resolve-from: 5.0.0
semver: 7.6.0 semver: 7.6.3
slash: 3.0.0 slash: 3.0.0
slugify: 1.6.6 slugify: 1.6.6
xcode: 3.0.1 xcode: 3.0.1
@@ -4905,10 +4905,6 @@ packages:
dev: false dev: false
optional: true optional: true
/@next/env@14.1.0:
resolution: {integrity: sha512-Py8zIo+02ht82brwwhTg36iogzFqGLPXlRGKQw5s+qP/kMNc4MAyDeEwBKDijk6zTIbegEgu8Qy7C1LboslQAw==}
dev: false
/@next/env@14.2.1: /@next/env@14.2.1:
resolution: {integrity: sha512-qsHJle3GU3CmVx7pUoXcghX4sRN+vINkbLdH611T8ZlsP//grzqVW87BSUgOZeSAD4q7ZdZicdwNe/20U2janA==} resolution: {integrity: sha512-qsHJle3GU3CmVx7pUoXcghX4sRN+vINkbLdH611T8ZlsP//grzqVW87BSUgOZeSAD4q7ZdZicdwNe/20U2janA==}
dev: false dev: false
@@ -4923,15 +4919,6 @@ packages:
glob: 7.1.7 glob: 7.1.7
dev: false dev: false
/@next/swc-darwin-arm64@14.1.0:
resolution: {integrity: sha512-nUDn7TOGcIeyQni6lZHfzNoo9S0euXnu0jhsbMOmMJUBfgsnESdjN97kM7cBqQxZa8L/bM9om/S5/1dzCrW6wQ==}
engines: {node: '>= 10'}
cpu: [arm64]
os: [darwin]
requiresBuild: true
dev: false
optional: true
/@next/swc-darwin-arm64@14.2.1: /@next/swc-darwin-arm64@14.2.1:
resolution: {integrity: sha512-kGjnjcIJehEcd3rT/3NAATJQndAEELk0J9GmGMXHSC75TMnvpOhONcjNHbjtcWE5HUQnIHy5JVkatrnYm1QhVw==} resolution: {integrity: sha512-kGjnjcIJehEcd3rT/3NAATJQndAEELk0J9GmGMXHSC75TMnvpOhONcjNHbjtcWE5HUQnIHy5JVkatrnYm1QhVw==}
engines: {node: '>= 10'} engines: {node: '>= 10'}
@@ -4950,15 +4937,6 @@ packages:
dev: false dev: false
optional: true optional: true
/@next/swc-darwin-x64@14.1.0:
resolution: {integrity: sha512-1jgudN5haWxiAl3O1ljUS2GfupPmcftu2RYJqZiMJmmbBT5M1XDffjUtRUzP4W3cBHsrvkfOFdQ71hAreNQP6g==}
engines: {node: '>= 10'}
cpu: [x64]
os: [darwin]
requiresBuild: true
dev: false
optional: true
/@next/swc-darwin-x64@14.2.1: /@next/swc-darwin-x64@14.2.1:
resolution: {integrity: sha512-dAdWndgdQi7BK2WSXrx4lae7mYcOYjbHJUhvOUnJjMNYrmYhxbbvJ2xElZpxNxdfA6zkqagIB9He2tQk+l16ew==} resolution: {integrity: sha512-dAdWndgdQi7BK2WSXrx4lae7mYcOYjbHJUhvOUnJjMNYrmYhxbbvJ2xElZpxNxdfA6zkqagIB9He2tQk+l16ew==}
engines: {node: '>= 10'} engines: {node: '>= 10'}
@@ -4977,15 +4955,6 @@ packages:
dev: false dev: false
optional: true optional: true
/@next/swc-linux-arm64-gnu@14.1.0:
resolution: {integrity: sha512-RHo7Tcj+jllXUbK7xk2NyIDod3YcCPDZxj1WLIYxd709BQ7WuRYl3OWUNG+WUfqeQBds6kvZYlc42NJJTNi4tQ==}
engines: {node: '>= 10'}
cpu: [arm64]
os: [linux]
requiresBuild: true
dev: false
optional: true
/@next/swc-linux-arm64-gnu@14.2.1: /@next/swc-linux-arm64-gnu@14.2.1:
resolution: {integrity: sha512-2ZctfnyFOGvTkoD6L+DtQtO3BfFz4CapoHnyLTXkOxbZkVRgg3TQBUjTD/xKrO1QWeydeo8AWfZRg8539qNKrg==} resolution: {integrity: sha512-2ZctfnyFOGvTkoD6L+DtQtO3BfFz4CapoHnyLTXkOxbZkVRgg3TQBUjTD/xKrO1QWeydeo8AWfZRg8539qNKrg==}
engines: {node: '>= 10'} engines: {node: '>= 10'}
@@ -5004,15 +4973,6 @@ packages:
dev: false dev: false
optional: true optional: true
/@next/swc-linux-arm64-musl@14.1.0:
resolution: {integrity: sha512-v6kP8sHYxjO8RwHmWMJSq7VZP2nYCkRVQ0qolh2l6xroe9QjbgV8siTbduED4u0hlk0+tjS6/Tuy4n5XCp+l6g==}
engines: {node: '>= 10'}
cpu: [arm64]
os: [linux]
requiresBuild: true
dev: false
optional: true
/@next/swc-linux-arm64-musl@14.2.1: /@next/swc-linux-arm64-musl@14.2.1:
resolution: {integrity: sha512-jazZXctiaanemy4r+TPIpFP36t1mMwWCKMsmrTRVChRqE6putyAxZA4PDujx0SnfvZHosjdkx9xIq9BzBB5tWg==} resolution: {integrity: sha512-jazZXctiaanemy4r+TPIpFP36t1mMwWCKMsmrTRVChRqE6putyAxZA4PDujx0SnfvZHosjdkx9xIq9BzBB5tWg==}
engines: {node: '>= 10'} engines: {node: '>= 10'}
@@ -5031,15 +4991,6 @@ packages:
dev: false dev: false
optional: true optional: true
/@next/swc-linux-x64-gnu@14.1.0:
resolution: {integrity: sha512-zJ2pnoFYB1F4vmEVlb/eSe+VH679zT1VdXlZKX+pE66grOgjmKJHKacf82g/sWE4MQ4Rk2FMBCRnX+l6/TVYzQ==}
engines: {node: '>= 10'}
cpu: [x64]
os: [linux]
requiresBuild: true
dev: false
optional: true
/@next/swc-linux-x64-gnu@14.2.1: /@next/swc-linux-x64-gnu@14.2.1:
resolution: {integrity: sha512-VjCHWCjsAzQAAo8lkBOLEIkBZFdfW+Z18qcQ056kL4KpUYc8o59JhLDCBlhg+hINQRgzQ2UPGma2AURGOH0+Qg==} resolution: {integrity: sha512-VjCHWCjsAzQAAo8lkBOLEIkBZFdfW+Z18qcQ056kL4KpUYc8o59JhLDCBlhg+hINQRgzQ2UPGma2AURGOH0+Qg==}
engines: {node: '>= 10'} engines: {node: '>= 10'}
@@ -5058,15 +5009,6 @@ packages:
dev: false dev: false
optional: true optional: true
/@next/swc-linux-x64-musl@14.1.0:
resolution: {integrity: sha512-rbaIYFt2X9YZBSbH/CwGAjbBG2/MrACCVu2X0+kSykHzHnYH5FjHxwXLkcoJ10cX0aWCEynpu+rP76x0914atg==}
engines: {node: '>= 10'}
cpu: [x64]
os: [linux]
requiresBuild: true
dev: false
optional: true
/@next/swc-linux-x64-musl@14.2.1: /@next/swc-linux-x64-musl@14.2.1:
resolution: {integrity: sha512-7HZKYKvAp4nAHiHIbY04finRqjeYvkITOGOurP1aLMexIFG/1+oCnqhGogBdc4lao/lkMW1c+AkwWSzSlLasqw==} resolution: {integrity: sha512-7HZKYKvAp4nAHiHIbY04finRqjeYvkITOGOurP1aLMexIFG/1+oCnqhGogBdc4lao/lkMW1c+AkwWSzSlLasqw==}
engines: {node: '>= 10'} engines: {node: '>= 10'}
@@ -5085,15 +5027,6 @@ packages:
dev: false dev: false
optional: true optional: true
/@next/swc-win32-arm64-msvc@14.1.0:
resolution: {integrity: sha512-o1N5TsYc8f/HpGt39OUQpQ9AKIGApd3QLueu7hXk//2xq5Z9OxmV6sQfNp8C7qYmiOlHYODOGqNNa0e9jvchGQ==}
engines: {node: '>= 10'}
cpu: [arm64]
os: [win32]
requiresBuild: true
dev: false
optional: true
/@next/swc-win32-arm64-msvc@14.2.1: /@next/swc-win32-arm64-msvc@14.2.1:
resolution: {integrity: sha512-YGHklaJ/Cj/F0Xd8jxgj2p8po4JTCi6H7Z3Yics3xJhm9CPIqtl8erlpK1CLv+HInDqEWfXilqatF8YsLxxA2Q==} resolution: {integrity: sha512-YGHklaJ/Cj/F0Xd8jxgj2p8po4JTCi6H7Z3Yics3xJhm9CPIqtl8erlpK1CLv+HInDqEWfXilqatF8YsLxxA2Q==}
engines: {node: '>= 10'} engines: {node: '>= 10'}
@@ -5112,15 +5045,6 @@ packages:
dev: false dev: false
optional: true optional: true
/@next/swc-win32-ia32-msvc@14.1.0:
resolution: {integrity: sha512-XXIuB1DBRCFwNO6EEzCTMHT5pauwaSj4SWs7CYnME57eaReAKBXCnkUE80p/pAZcewm7hs+vGvNqDPacEXHVkw==}
engines: {node: '>= 10'}
cpu: [ia32]
os: [win32]
requiresBuild: true
dev: false
optional: true
/@next/swc-win32-ia32-msvc@14.2.1: /@next/swc-win32-ia32-msvc@14.2.1:
resolution: {integrity: sha512-o+ISKOlvU/L43ZhtAAfCjwIfcwuZstiHVXq/BDsZwGqQE0h/81td95MPHliWCnFoikzWcYqh+hz54ZB2FIT8RA==} resolution: {integrity: sha512-o+ISKOlvU/L43ZhtAAfCjwIfcwuZstiHVXq/BDsZwGqQE0h/81td95MPHliWCnFoikzWcYqh+hz54ZB2FIT8RA==}
engines: {node: '>= 10'} engines: {node: '>= 10'}
@@ -5130,15 +5054,6 @@ packages:
dev: false dev: false
optional: true optional: true
/@next/swc-win32-x64-msvc@14.1.0:
resolution: {integrity: sha512-9WEbVRRAqJ3YFVqEZIxUqkiO8l1nool1LmNxygr5HWF8AcSYsEpneUDhmjUVJEzO2A04+oPtZdombzzPPkTtgg==}
engines: {node: '>= 10'}
cpu: [x64]
os: [win32]
requiresBuild: true
dev: false
optional: true
/@next/swc-win32-x64-msvc@14.2.1: /@next/swc-win32-x64-msvc@14.2.1:
resolution: {integrity: sha512-GmRoTiLcvCLifujlisknv4zu9/C4i9r0ktsA8E51EMqJL4bD4CpO7lDYr7SrUxCR0tS4RVcrqKmCak24T0ohaw==} resolution: {integrity: sha512-GmRoTiLcvCLifujlisknv4zu9/C4i9r0ktsA8E51EMqJL4bD4CpO7lDYr7SrUxCR0tS4RVcrqKmCak24T0ohaw==}
engines: {node: '>= 10'} engines: {node: '>= 10'}
@@ -8702,7 +8617,7 @@ packages:
hermes-profile-transformer: 0.0.6 hermes-profile-transformer: 0.0.6
node-stream-zip: 1.15.0 node-stream-zip: 1.15.0
ora: 5.4.1 ora: 5.4.1
semver: 7.6.0 semver: 7.6.3
strip-ansi: 5.2.0 strip-ansi: 5.2.0
wcwidth: 1.0.1 wcwidth: 1.0.1
yaml: 2.3.4 yaml: 2.3.4
@@ -8780,7 +8695,7 @@ packages:
node-fetch: 2.7.0 node-fetch: 2.7.0
open: 6.4.0 open: 6.4.0
ora: 5.4.1 ora: 5.4.1
semver: 7.6.0 semver: 7.6.3
shell-quote: 1.8.1 shell-quote: 1.8.1
sudo-prompt: 9.2.1 sudo-prompt: 9.2.1
transitivePeerDependencies: transitivePeerDependencies:
@@ -8815,7 +8730,7 @@ packages:
fs-extra: 8.1.0 fs-extra: 8.1.0
graceful-fs: 4.2.11 graceful-fs: 4.2.11
prompts: 2.4.2 prompts: 2.4.2
semver: 7.6.0 semver: 7.6.3
transitivePeerDependencies: transitivePeerDependencies:
- bufferutil - bufferutil
- encoding - encoding
@@ -9430,12 +9345,6 @@ packages:
tslib: 2.7.0 tslib: 2.7.0
dev: false dev: false
/@swc/helpers@0.5.2:
resolution: {integrity: sha512-E4KcWTpoLHqwPHLxidpOqQbcrZVgi0rsmmZXUle1jXmJfuIf/UWpczUJ7MZZ5tlxytgJXyp0w4PGkkeLiuIdZw==}
dependencies:
tslib: 2.7.0
dev: false
/@swc/helpers@0.5.5: /@swc/helpers@0.5.5:
resolution: {integrity: sha512-KGYxvIOXcceOAbEk4bi/dVLEK9z8sZ0uBB3Il5b1rhfClSpcX0yfRO0KmTkqR2cnQDymwLB+25ZyMzICg/cm/A==} resolution: {integrity: sha512-KGYxvIOXcceOAbEk4bi/dVLEK9z8sZ0uBB3Il5b1rhfClSpcX0yfRO0KmTkqR2cnQDymwLB+25ZyMzICg/cm/A==}
dependencies: dependencies:
@@ -17202,46 +17111,6 @@ packages:
react-dom: 18.3.1(react@18.3.1) react-dom: 18.3.1(react@18.3.1)
dev: false dev: false
/next@14.1.0(@opentelemetry/api@1.8.0)(react-dom@18.2.0)(react@18.2.0):
resolution: {integrity: sha512-wlzrsbfeSU48YQBjZhDzOwhWhGsy+uQycR8bHAOt1LY1bn3zZEcDyHQOEoN3aWzQ8LHCAJ1nqrWCc9XF2+O45Q==}
engines: {node: '>=18.17.0'}
hasBin: true
peerDependencies:
'@opentelemetry/api': ^1.1.0
react: ^18.2.0
react-dom: ^18.2.0
sass: ^1.3.0
peerDependenciesMeta:
'@opentelemetry/api':
optional: true
sass:
optional: true
dependencies:
'@next/env': 14.1.0
'@opentelemetry/api': 1.8.0
'@swc/helpers': 0.5.2
busboy: 1.6.0
caniuse-lite: 1.0.30001596
graceful-fs: 4.2.11
postcss: 8.4.31
react: 18.2.0
react-dom: 18.2.0(react@18.2.0)
styled-jsx: 5.1.1(react@18.2.0)
optionalDependencies:
'@next/swc-darwin-arm64': 14.1.0
'@next/swc-darwin-x64': 14.1.0
'@next/swc-linux-arm64-gnu': 14.1.0
'@next/swc-linux-arm64-musl': 14.1.0
'@next/swc-linux-x64-gnu': 14.1.0
'@next/swc-linux-x64-musl': 14.1.0
'@next/swc-win32-arm64-msvc': 14.1.0
'@next/swc-win32-ia32-msvc': 14.1.0
'@next/swc-win32-x64-msvc': 14.1.0
transitivePeerDependencies:
- '@babel/core'
- babel-plugin-macros
dev: false
/next@14.2.1(@opentelemetry/api@1.8.0)(react-dom@18.2.0)(react@18.2.0): /next@14.2.1(@opentelemetry/api@1.8.0)(react-dom@18.2.0)(react@18.2.0):
resolution: {integrity: sha512-SF3TJnKdH43PMkCcErLPv+x/DY1YCklslk3ZmwaVoyUfDgHKexuKlf9sEfBQ69w+ue8jQ3msLb+hSj1T19hGag==} resolution: {integrity: sha512-SF3TJnKdH43PMkCcErLPv+x/DY1YCklslk3ZmwaVoyUfDgHKexuKlf9sEfBQ69w+ue8jQ3msLb+hSj1T19hGag==}
engines: {node: '>=18.17.0'} engines: {node: '>=18.17.0'}
@@ -20919,7 +20788,7 @@ packages:
execa: 5.1.1 execa: 5.1.1
globby: 11.1.0 globby: 11.1.0
joycon: 3.1.1 joycon: 3.1.1
postcss-load-config: 4.0.2(postcss@8.4.35) postcss-load-config: 4.0.2(postcss@8.4.47)
resolve-from: 5.0.0 resolve-from: 5.0.0
rollup: 4.12.0 rollup: 4.12.0
source-map: 0.8.0-beta.0 source-map: 0.8.0-beta.0
@@ -21254,7 +21123,7 @@ packages:
dependencies: dependencies:
browserslist: 4.23.0 browserslist: 4.23.0
escalade: 3.1.2 escalade: 3.1.2
picocolors: 1.0.0 picocolors: 1.1.1
/update-browserslist-db@1.1.1(browserslist@4.24.2): /update-browserslist-db@1.1.1(browserslist@4.24.2):
resolution: {integrity: sha512-R8UzCaa9Az+38REPiJ1tXlImTJXlVfgHZsglwBD/k6nj76ctsH1E3q4doGrukiLQd3sGQYu56r5+lo5r94l29A==} resolution: {integrity: sha512-R8UzCaa9Az+38REPiJ1tXlImTJXlVfgHZsglwBD/k6nj76ctsH1E3q4doGrukiLQd3sGQYu56r5+lo5r94l29A==}