feature(dashboard): add ability to filter out events by profile id and ip (#101)
This commit is contained in:
committed by
GitHub
parent
27ee623584
commit
f4ad97d87d
@@ -27,31 +27,21 @@ import { ModalContent, ModalHeader } from './Modal/Container';
|
||||
|
||||
const validation = z.object({
|
||||
name: z.string().min(1),
|
||||
cors: z.string().min(1).or(z.literal('')),
|
||||
projectId: z.string(),
|
||||
type: z.enum(['read', 'write', 'root']),
|
||||
crossDomain: z.boolean().optional(),
|
||||
});
|
||||
|
||||
type IForm = z.infer<typeof validation>;
|
||||
|
||||
interface Props {
|
||||
projectId: string;
|
||||
}
|
||||
export default function AddClient(props: Props) {
|
||||
export default function AddClient() {
|
||||
const { organizationId, projectId } = useAppParams();
|
||||
const router = useRouter();
|
||||
const form = useForm<IForm>({
|
||||
resolver: zodResolver(validation),
|
||||
defaultValues: {
|
||||
name: '',
|
||||
cors: '',
|
||||
projectId: props.projectId ?? projectId,
|
||||
type: 'write',
|
||||
crossDomain: undefined,
|
||||
},
|
||||
});
|
||||
const [hasDomain, setHasDomain] = useState(true);
|
||||
const mutation = api.client.create.useMutation({
|
||||
onError: handleError,
|
||||
onSuccess() {
|
||||
@@ -63,19 +53,11 @@ export default function AddClient(props: Props) {
|
||||
organizationId,
|
||||
});
|
||||
const onSubmit: SubmitHandler<IForm> = (values) => {
|
||||
if (hasDomain && values.cors === '') {
|
||||
return form.setError('cors', {
|
||||
type: 'required',
|
||||
message: 'Please add a domain',
|
||||
});
|
||||
}
|
||||
mutation.mutate({
|
||||
name: values.name,
|
||||
cors: hasDomain ? values.cors : null,
|
||||
projectId: values.projectId,
|
||||
organizationId,
|
||||
type: values.type,
|
||||
crossDomain: values.crossDomain,
|
||||
projectId,
|
||||
organizationId,
|
||||
});
|
||||
};
|
||||
|
||||
@@ -106,33 +88,6 @@ export default function AddClient(props: Props) {
|
||||
className="flex flex-col gap-4"
|
||||
onSubmit={form.handleSubmit(onSubmit)}
|
||||
>
|
||||
<div>
|
||||
<Controller
|
||||
control={form.control}
|
||||
name="projectId"
|
||||
render={({ field }) => {
|
||||
return (
|
||||
<div>
|
||||
<Label>Project</Label>
|
||||
<Combobox
|
||||
{...field}
|
||||
className="w-full"
|
||||
onChange={(value) => {
|
||||
field.onChange(value);
|
||||
}}
|
||||
items={
|
||||
query.data?.map((item) => ({
|
||||
value: item.id,
|
||||
label: item.name,
|
||||
})) ?? []
|
||||
}
|
||||
placeholder="Select a project"
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<Label>Client name</Label>
|
||||
<Input
|
||||
@@ -142,67 +97,6 @@ export default function AddClient(props: Props) {
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<Label className="flex items-center justify-between">
|
||||
<span>Domain(s)</span>
|
||||
<Switch checked={hasDomain} onCheckedChange={setHasDomain} />
|
||||
</Label>
|
||||
<AnimateHeight open={hasDomain}>
|
||||
<Controller
|
||||
name="cors"
|
||||
control={form.control}
|
||||
render={({ field }) => (
|
||||
<TagInput
|
||||
{...field}
|
||||
error={form.formState.errors.cors?.message}
|
||||
placeholder="Add a domain"
|
||||
value={field.value?.split(',') ?? []}
|
||||
renderTag={(tag) =>
|
||||
tag === '*' ? 'Allow all domains' : tag
|
||||
}
|
||||
onChange={(newValue) => {
|
||||
field.onChange(
|
||||
newValue
|
||||
.map((item) => {
|
||||
const trimmed = item.trim();
|
||||
if (
|
||||
trimmed.startsWith('http://') ||
|
||||
trimmed.startsWith('https://') ||
|
||||
trimmed === '*'
|
||||
) {
|
||||
return trimmed;
|
||||
}
|
||||
return `https://${trimmed}`;
|
||||
})
|
||||
.join(','),
|
||||
);
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
<Controller
|
||||
name="crossDomain"
|
||||
control={form.control}
|
||||
render={({ field }) => {
|
||||
return (
|
||||
<CheckboxInput
|
||||
className="mt-4"
|
||||
ref={field.ref}
|
||||
onBlur={field.onBlur}
|
||||
defaultChecked={field.value}
|
||||
onCheckedChange={field.onChange}
|
||||
>
|
||||
<div>Enable cross domain support</div>
|
||||
<div className="font-normal text-muted-foreground">
|
||||
This will let you track users across multiple domains
|
||||
</div>
|
||||
</CheckboxInput>
|
||||
);
|
||||
}}
|
||||
/>
|
||||
</AnimateHeight>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<Controller
|
||||
control={form.control}
|
||||
@@ -233,7 +127,7 @@ export default function AddClient(props: Props) {
|
||||
]}
|
||||
placeholder="Select a project"
|
||||
/>
|
||||
<p className="mt-1 text-sm text-muted-foreground">
|
||||
<p className="mt-2 text-sm text-muted-foreground">
|
||||
{field.value === 'write' &&
|
||||
'Write: Is the default client type and is used for ingestion of data'}
|
||||
{field.value === 'read' &&
|
||||
|
||||
@@ -1,67 +1,164 @@
|
||||
'use client';
|
||||
|
||||
import AnimateHeight from '@/components/animate-height';
|
||||
import { ButtonContainer } from '@/components/button-container';
|
||||
import { InputWithLabel } from '@/components/forms/input-with-label';
|
||||
import { InputWithLabel, WithLabel } from '@/components/forms/input-with-label';
|
||||
import TagInput from '@/components/forms/tag-input';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { CheckboxInput } from '@/components/ui/checkbox';
|
||||
import { Input } from '@/components/ui/input';
|
||||
import { Label } from '@/components/ui/label';
|
||||
import { Switch } from '@/components/ui/switch';
|
||||
import { useAppParams } from '@/hooks/useAppParams';
|
||||
import { api, handleError } from '@/trpc/client';
|
||||
import { zodResolver } from '@hookform/resolvers/zod';
|
||||
import { useRouter } from 'next/navigation';
|
||||
import { useForm } from 'react-hook-form';
|
||||
import type { IServiceProjectWithClients } from '@openpanel/db';
|
||||
import { zProject } from '@openpanel/validation';
|
||||
import { SaveIcon } from 'lucide-react';
|
||||
import { useState } from 'react';
|
||||
import { Controller, useForm } from 'react-hook-form';
|
||||
import { toast } from 'sonner';
|
||||
import { z } from 'zod';
|
||||
|
||||
import { popModal } from '.';
|
||||
import type { z } from 'zod';
|
||||
import { ModalContent, ModalHeader } from './Modal/Container';
|
||||
|
||||
const validator = z.object({
|
||||
name: z.string().min(1),
|
||||
});
|
||||
type Props = { project: IServiceProjectWithClients };
|
||||
|
||||
const validator = zProject.pick({
|
||||
name: true,
|
||||
domain: true,
|
||||
cors: true,
|
||||
crossDomain: true,
|
||||
});
|
||||
type IForm = z.infer<typeof validator>;
|
||||
|
||||
export default function AddProject() {
|
||||
const { organizationId } = useAppParams();
|
||||
const router = useRouter();
|
||||
const mutation = api.project.create.useMutation({
|
||||
onError: handleError,
|
||||
onSuccess() {
|
||||
router.refresh();
|
||||
toast('Success', {
|
||||
description: 'Project created! Lets create a client for it 🤘',
|
||||
});
|
||||
popModal();
|
||||
},
|
||||
});
|
||||
const { register, handleSubmit, formState } = useForm<IForm>({
|
||||
const [hasDomain, setHasDomain] = useState(true);
|
||||
const form = useForm<IForm>({
|
||||
resolver: zodResolver(validator),
|
||||
defaultValues: {
|
||||
name: '',
|
||||
domain: '',
|
||||
cors: [],
|
||||
crossDomain: false,
|
||||
},
|
||||
});
|
||||
const mutation = api.project.create.useMutation({
|
||||
onError: handleError,
|
||||
onSuccess: () => {
|
||||
toast.success('Project created');
|
||||
},
|
||||
});
|
||||
|
||||
const onSubmit = (values: IForm) => {
|
||||
if (hasDomain) {
|
||||
let error = false;
|
||||
if (values.cors.length === 0) {
|
||||
form.setError('cors', {
|
||||
type: 'required',
|
||||
message: 'Please add at least one cors domain',
|
||||
});
|
||||
error = true;
|
||||
}
|
||||
|
||||
if (!values.domain) {
|
||||
form.setError('domain', {
|
||||
type: 'required',
|
||||
message: 'Please add a domain',
|
||||
});
|
||||
error = true;
|
||||
}
|
||||
|
||||
if (error) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
mutation.mutate({
|
||||
...(hasDomain ? values : { ...values, cors: [], domain: null }),
|
||||
organizationId,
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<ModalContent>
|
||||
<ModalHeader title="Create project" />
|
||||
<form
|
||||
onSubmit={handleSubmit((values) => {
|
||||
mutation.mutate({
|
||||
...values,
|
||||
organizationId,
|
||||
});
|
||||
onSubmit={form.handleSubmit(onSubmit, (errors) => {
|
||||
console.log(errors);
|
||||
})}
|
||||
className="col gap-4"
|
||||
>
|
||||
<div className="flex flex-col gap-4">
|
||||
<InputWithLabel
|
||||
label="Name"
|
||||
placeholder="Name"
|
||||
{...register('name')}
|
||||
/>
|
||||
<InputWithLabel label="Name" {...form.register('name')} />
|
||||
|
||||
<div className="-mb-2 flex gap-2 items-center justify-between">
|
||||
<Label className="mb-0">Domain</Label>
|
||||
<Switch checked={hasDomain} onCheckedChange={setHasDomain} />
|
||||
</div>
|
||||
<AnimateHeight open={hasDomain}>
|
||||
<Input
|
||||
placeholder="Domain"
|
||||
{...form.register('domain')}
|
||||
className="mb-4"
|
||||
error={form.formState.errors.domain?.message}
|
||||
/>
|
||||
|
||||
<Controller
|
||||
name="cors"
|
||||
control={form.control}
|
||||
render={({ field }) => (
|
||||
<WithLabel label="Cors">
|
||||
<TagInput
|
||||
{...field}
|
||||
id="Cors"
|
||||
error={form.formState.errors.cors?.message}
|
||||
placeholder="Add a domain"
|
||||
value={field.value ?? []}
|
||||
renderTag={(tag) => (tag === '*' ? 'Allow all domains' : tag)}
|
||||
onChange={(newValue) => {
|
||||
field.onChange(
|
||||
newValue.map((item) => {
|
||||
const trimmed = item.trim();
|
||||
if (
|
||||
trimmed.startsWith('http://') ||
|
||||
trimmed.startsWith('https://') ||
|
||||
trimmed === '*'
|
||||
) {
|
||||
return trimmed;
|
||||
}
|
||||
return `https://${trimmed}`;
|
||||
}),
|
||||
);
|
||||
}}
|
||||
/>
|
||||
</WithLabel>
|
||||
)}
|
||||
/>
|
||||
<Controller
|
||||
name="crossDomain"
|
||||
control={form.control}
|
||||
render={({ field }) => {
|
||||
return (
|
||||
<CheckboxInput
|
||||
className="mt-4"
|
||||
ref={field.ref}
|
||||
onBlur={field.onBlur}
|
||||
defaultChecked={field.value}
|
||||
onCheckedChange={field.onChange}
|
||||
>
|
||||
<div>Enable cross domain support</div>
|
||||
<div className="font-normal text-muted-foreground">
|
||||
This will let you track users across multiple domains
|
||||
</div>
|
||||
</CheckboxInput>
|
||||
);
|
||||
}}
|
||||
/>
|
||||
</AnimateHeight>
|
||||
|
||||
<ButtonContainer>
|
||||
<Button type="button" variant="outline" onClick={() => popModal()}>
|
||||
Cancel
|
||||
</Button>
|
||||
<Button type="submit" disabled={!formState.isDirty}>
|
||||
Create
|
||||
<Button loading={mutation.isLoading} type="submit" icon={SaveIcon}>
|
||||
Save
|
||||
</Button>
|
||||
</ButtonContainer>
|
||||
</form>
|
||||
|
||||
Reference in New Issue
Block a user