diff --git a/components/common/WorldMap.js b/components/common/WorldMap.js index 31c6686b..985160d8 100644 --- a/components/common/WorldMap.js +++ b/components/common/WorldMap.js @@ -4,7 +4,7 @@ import { ComposableMap, Geographies, Geography, ZoomableGroup } from 'react-simp import classNames from 'classnames'; import { colord } from 'colord'; import HoverTooltip from 'components/common/HoverTooltip'; -import { ISO_COUNTRIES, THEME_COLORS, MAP_FILE } from 'lib/constants'; +import { ISO_COUNTRIES, MAP_FILE } from 'lib/constants'; import useTheme from 'hooks/useTheme'; import useCountryNames from 'hooks/useCountryNames'; import useLocale from 'hooks/useLocale'; @@ -15,16 +15,7 @@ import styles from './WorldMap.module.css'; export function WorldMap({ data, className }) { const { basePath } = useRouter(); const [tooltip, setTooltip] = useState(); - const { theme } = useTheme(); - const colors = useMemo( - () => ({ - baseColor: THEME_COLORS[theme].primary, - fillColor: THEME_COLORS[theme].gray100, - strokeColor: THEME_COLORS[theme].primary, - hoverColor: THEME_COLORS[theme].primary, - }), - [theme], - ); + const { theme, colors } = useTheme(); const { locale } = useLocale(); const countryNames = useCountryNames(locale); const metrics = useMemo(() => (data ? percentFilter(data) : []), [data]); @@ -34,10 +25,10 @@ export function WorldMap({ data, className }) { const country = metrics?.find(({ x }) => x === code); if (!country) { - return colors.fillColor; + return colors.map.fillColor; } - return colord(colors.baseColor) + return colord(colors.map.baseColor) [theme === 'light' ? 'lighten' : 'darken'](0.4 * (1.0 - country.z / 100)) .toHex(); } @@ -70,11 +61,11 @@ export function WorldMap({ data, className }) { key={geo.rsmKey} geography={geo} fill={getFillColor(code)} - stroke={colors.strokeColor} + stroke={colors.map.strokeColor} opacity={getOpacity(code)} style={{ default: { outline: 'none' }, - hover: { outline: 'none', fill: colors.hoverColor }, + hover: { outline: 'none', fill: colors.map.hoverColor }, pressed: { outline: 'none' }, }} onMouseOver={() => handleHover(code)} diff --git a/components/messages.js b/components/messages.js index 0de3a44c..adae6aa2 100644 --- a/components/messages.js +++ b/components/messages.js @@ -128,6 +128,7 @@ export const labels = defineMessages({ add: { id: 'label.add', defaultMessage: 'Add' }, window: { id: 'label.window', defaultMessage: 'Window' }, addUrl: { id: 'label.add-url', defaultMessage: 'Add URL' }, + runQuery: { id: 'label.run-query', defaultMessage: 'Run query' }, }); export const messages = defineMessages({ diff --git a/components/pages/reports/Report.js b/components/pages/reports/Report.js index 4e3fcd04..0a98ef75 100644 --- a/components/pages/reports/Report.js +++ b/components/pages/reports/Report.js @@ -1,11 +1,19 @@ +import { createContext } from 'react'; import Page from 'components/layout/Page'; import styles from './reports.module.css'; +import { useReport } from 'hooks'; + +export const ReportContext = createContext(null); + +export function Report({ reportId, defaultParameters, children, ...props }) { + const report = useReport(reportId, defaultParameters); -export function Report({ children, ...props }) { return ( - - {children} - + + + {children} + + ); } diff --git a/components/pages/reports/ReportHeader.js b/components/pages/reports/ReportHeader.js index 7da47245..bf2c7fa8 100644 --- a/components/pages/reports/ReportHeader.js +++ b/components/pages/reports/ReportHeader.js @@ -1,42 +1,62 @@ -import { Flexbox, Icon, LoadingButton, Text, useToast } from 'react-basics'; +import { useContext } from 'react'; +import { useRouter } from 'next/router'; +import { Flexbox, Icon, LoadingButton, InlineEditField, useToast } from 'react-basics'; import WebsiteSelect from 'components/input/WebsiteSelect'; import PageHeader from 'components/layout/PageHeader'; import DateFilter from 'components/input/DateFilter'; import { parseDateRange } from 'lib/date'; -import { updateReport } from 'store/reports'; import { useMessages, useApi } from 'hooks'; +import { ReportContext } from './Report'; import styles from './reports.module.css'; -export function ReportHeader({ report, icon }) { +export function ReportHeader({ icon }) { + const { report, updateReport } = useContext(ReportContext); const { formatMessage, labels, messages } = useMessages(); const { toast, showToast } = useToast(); const { post, useMutation } = useApi(); - const { mutate, isLoading } = useMutation(data => post(`/reports`, data)); + const router = useRouter(); + const { mutate: create, isLoading: isCreating } = useMutation(data => post(`/reports`, data)); + const { mutate: update, isLoading: isUpdating } = useMutation(data => + post(`/reports/${data.id}`, data), + ); - const { id, websiteId, name, parameters } = report || {}; - const { value, startDate, endDate } = parameters?.dateRange || {}; + const { websiteId, name, dateRange } = report || {}; + const { value, startDate, endDate } = dateRange || {}; const handleSelect = websiteId => { - updateReport(id, { websiteId }); + updateReport({ websiteId }); }; const handleDateChange = value => { - updateReport(id, { parameters: { dateRange: { ...parseDateRange(value) } } }); + updateReport({ dateRange: { ...parseDateRange(value) } }); }; const handleSave = async () => { - mutate(report, { - onSuccess: async () => { - showToast({ message: formatMessage(messages.saved), variant: 'success' }); - }, - }); + if (!report.id) { + create(report, { + onSuccess: async ({ id }) => { + router.push(`/reports/${id}`, null, { shallow: true }); + showToast({ message: formatMessage(messages.saved), variant: 'success' }); + }, + }); + } else { + update(report, { + onSuccess: async () => { + showToast({ message: formatMessage(messages.saved), variant: 'success' }); + }, + }); + } + }; + + const handleNameChange = name => { + updateReport({ name }); }; const Title = () => { return ( <> {icon} - {name} + ); }; @@ -54,7 +74,7 @@ export function ReportHeader({ report, icon }) { diff --git a/components/pages/reports/funnel/AddUrlForm.js b/components/pages/reports/funnel/AddUrlForm.js new file mode 100644 index 00000000..07f34867 --- /dev/null +++ b/components/pages/reports/funnel/AddUrlForm.js @@ -0,0 +1,45 @@ +import { useState } from 'react'; +import { useMessages } from 'hooks'; +import { Button, Form, FormButtons, FormRow, TextField } from 'react-basics'; + +export function AddUrlForm({ defaultValue = '', onSave, onClose }) { + const [url, setUrl] = useState(defaultValue); + const { formatMessage, labels } = useMessages(); + + const handleSave = () => { + onSave?.(url); + setUrl(''); + onClose(); + }; + + const handleChange = e => { + setUrl(e.target.value); + }; + + const handleClose = () => { + setUrl(''); + onClose(); + }; + + return ( +
+ + + + + + + +
+ ); +} + +export default AddUrlForm; diff --git a/components/pages/reports/funnel/FunnelChart.js b/components/pages/reports/funnel/FunnelChart.js index aecd48a7..3dfeb19e 100644 --- a/components/pages/reports/funnel/FunnelChart.js +++ b/components/pages/reports/funnel/FunnelChart.js @@ -1,16 +1,18 @@ -import { useCallback, useMemo } from 'react'; +import { useCallback, useContext, useMemo } from 'react'; import { Loading } from 'react-basics'; import useMessages from 'hooks/useMessages'; import useTheme from 'hooks/useTheme'; import BarChart from 'components/metrics/BarChart'; import { formatLongNumber } from 'lib/format'; import styles from './FunnelChart.module.css'; +import { ReportContext } from '../Report'; -export function FunnelChart({ report, data, loading, className }) { +export function FunnelChart({ className, loading }) { + const { report } = useContext(ReportContext); const { formatMessage, labels } = useMessages(); const { colors } = useTheme(); - const { parameters } = report || {}; + const { parameters, data } = report || {}; const renderXLabel = useCallback( (label, index) => { diff --git a/components/pages/reports/funnel/FunnelParameters.js b/components/pages/reports/funnel/FunnelParameters.js index 3caf6a8e..c87d52e4 100644 --- a/components/pages/reports/funnel/FunnelParameters.js +++ b/components/pages/reports/funnel/FunnelParameters.js @@ -1,12 +1,11 @@ +import { useContext, useRef, useState } from 'react'; import { useMessages } from 'hooks'; import { - Button, Icon, Form, FormButtons, FormInput, FormRow, - ModalTrigger, Modal, SubmitButton, Text, @@ -14,30 +13,36 @@ import { Tooltip, } from 'react-basics'; import Icons from 'components/icons'; -import { updateReport } from 'store/reports'; -import { useRef, useState } from 'react'; +import AddUrlForm from './AddUrlForm'; +import { ReportContext } from 'components/pages/reports/Report'; import styles from './FunnelParameters.module.css'; -export function FunnelParameters({ report }) { +export function FunnelParameters() { + const { report, runReport, updateReport, isRunning } = useContext(ReportContext); const { formatMessage, labels } = useMessages(); + const [show, setShow] = useState(false); const ref = useRef(null); - const { id, websiteId, parameters, isLoading } = report || {}; + const { websiteId, parameters } = report || {}; const queryDisabled = !websiteId || parameters?.urls?.length < 2; const handleSubmit = values => { - updateReport(id, { parameters: values, isLoading: false, update: Date.now() }); + runReport(values); }; - const handleAdd = url => { - updateReport(id, { parameters: { ...parameters, urls: parameters.urls.concat(url) } }); + const handleAddUrl = url => { + updateReport({ parameters: { ...parameters, urls: parameters.urls.concat(url) } }); }; - const handleRemove = index => { + const handleRemoveUrl = (index, e) => { + e.stopPropagation(); const urls = [...parameters.urls]; urls.splice(index, 1); - updateReport(id, { parameters: { ...parameters, urls } }); + updateReport({ parameters: { ...parameters, urls } }); }; + const showAddForm = () => setShow(true); + const hideAddForm = () => setShow(false); + return ( <>
@@ -49,72 +54,49 @@ export function FunnelParameters({ report }) { - }> + }>
- {parameters?.urls.map((url, index) => { + {parameters?.urls?.map((url, index) => { return (
{url} - handleRemove(index)}> - - + + + + +
); })}
- - {formatMessage(labels.query)} + + {formatMessage(labels.runQuery)} + {show && ( + + + + )} ); } -function AddURLButton({ onAdd }) { - const [url, setUrl] = useState(''); +function AddUrlButton({ onClick }) { const { formatMessage, labels } = useMessages(); - const handleAdd = close => { - onAdd?.(url); - setUrl(''); - close(); - }; - - const handleChange = e => { - setUrl(e.target.value); - }; - const handleClose = close => { - setUrl(''); - close(); - }; - return ( - - - - - - {close => { - return ( -
- - - - - - - -
- ); - }} -
-
+ + +
); } diff --git a/components/pages/reports/funnel/FunnelParameters.module.css b/components/pages/reports/funnel/FunnelParameters.module.css index 25ef0241..98ae0831 100644 --- a/components/pages/reports/funnel/FunnelParameters.module.css +++ b/components/pages/reports/funnel/FunnelParameters.module.css @@ -1,7 +1,7 @@ .urls { display: flex; flex-direction: column; - gap: 10px; + gap: 16px; } .url { @@ -14,3 +14,7 @@ border-radius: var(--border-radius); box-shadow: 1px 1px 1px var(--base400); } + +.icon { + align-self: center; +} diff --git a/components/pages/reports/funnel/FunnelReport.js b/components/pages/reports/funnel/FunnelReport.js index da422d54..26a81a09 100644 --- a/components/pages/reports/funnel/FunnelReport.js +++ b/components/pages/reports/funnel/FunnelReport.js @@ -1,41 +1,29 @@ +import { useContext } from 'react'; import FunnelChart from './FunnelChart'; import FunnelTable from './FunnelTable'; import FunnelParameters from './FunnelParameters'; -import Report from '../Report'; +import Report, { ReportContext } from '../Report'; import ReportHeader from '../ReportHeader'; import ReportMenu from '../ReportMenu'; import ReportBody from '../ReportBody'; import Funnel from 'assets/funnel.svg'; import { useReport } from 'hooks'; -import useApi from 'hooks/useApi'; + +const defaultParameters = { + type: 'funnel', + parameters: { window: 60, urls: ['/', '/docs'] }, +}; export default function FunnelReport({ reportId }) { - const report = useReport(reportId, { window: 60, urls: ['/', '/docs'] }); - const { post, useQuery } = useApi(); - const { data, isLoading, error } = useQuery( - ['report:funnel', report?.update], - () => { - const { websiteId, parameters } = report || {}; - - return post(`/reports/funnel`, { - websiteId: websiteId, - ...parameters, - startAt: +parameters.dateRange.startDate, - endAt: +parameters.dateRange.endDate, - }); - }, - { enabled: !!report?.update }, - ); - return ( - - } report={report} /> + + } /> - + - - + + ); diff --git a/components/pages/reports/funnel/FunnelTable.js b/components/pages/reports/funnel/FunnelTable.js index 7b726e48..ff6bdfb5 100644 --- a/components/pages/reports/funnel/FunnelTable.js +++ b/components/pages/reports/funnel/FunnelTable.js @@ -1,12 +1,15 @@ +import { useContext } from 'react'; import DataTable from 'components/metrics/DataTable'; import { useMessages } from 'hooks'; +import { ReportContext } from '../Report'; -export function FunnelTable({ data }) { +export function FunnelTable() { + const { report } = useContext(ReportContext); const { formatMessage, labels } = useMessages(); return ( state[id], [id]); - const report = useStore(selector); + const loadReport = async id => { + const data = await get(`/reports/${id}`); + + setReport(data); + }; + + const runReport = useCallback( + async parameters => { + const { websiteId, type, dateRange } = report; + setIsRunning(true); + + const data = await post(`/reports/${type}`, { + websiteId: websiteId, + ...parameters, + startAt: +dateRange?.startDate, + endAt: +dateRange?.endDate, + }); + + setReport( + produce(state => { + state.parameters = parameters; + state.data = data; + + return state; + }), + ); + + setIsRunning(false); + }, + [report], + ); + + const updateReport = useCallback( + async data => { + setReport( + produce(state => { + const { parameters, ...rest } = data; + + if (parameters) { + state.parameters = { ...state.parameters, ...parameters }; + } + + for (const key in rest) { + state[key] = rest[key]; + } + + return state; + }), + ); + }, + [report], + ); useEffect(() => { - if (!report) { - const newReport = createReport(defaultParameters); - setId(newReport.id); + if (!reportId) { + setReport({ ...baseParameters, ...defaultParameters }); + } else { + loadReport(reportId); } }, []); - return report; + return { report, runReport, updateReport, isRunning }; } export default useReport; diff --git a/hooks/useTheme.js b/hooks/useTheme.js index b8b51226..7e40f601 100644 --- a/hooks/useTheme.js +++ b/hooks/useTheme.js @@ -17,6 +17,9 @@ export function useTheme() { const primaryColor = colord(THEME_COLORS[theme].primary); const colors = { + theme: { + ...THEME_COLORS[theme], + }, chart: { text: THEME_COLORS[theme].gray700, line: THEME_COLORS[theme].gray200, @@ -33,6 +36,12 @@ export function useTheme() { hoverBorderColor: primaryColor.toRgbString(), }, }, + map: { + baseColor: THEME_COLORS[theme].primary, + fillColor: THEME_COLORS[theme].gray100, + strokeColor: THEME_COLORS[theme].primary, + hoverColor: THEME_COLORS[theme].primary, + }, }; function saveTheme(value) { diff --git a/lib/auth.ts b/lib/auth.ts index 37dc6acb..bf01a1ab 100644 --- a/lib/auth.ts +++ b/lib/auth.ts @@ -1,4 +1,4 @@ -import { UserReport } from '@prisma/client'; +import { Report } from '@prisma/client'; import redis from '@umami/redis-client'; import debug from 'debug'; import { PERMISSIONS, ROLE_PERMISSIONS, SHARE_TOKEN_HEADER } from 'lib/constants'; @@ -135,28 +135,28 @@ export async function canDeleteWebsite({ user }: Auth, websiteId: string) { return false; } -export async function canViewUserReport(auth: Auth, userReport: UserReport) { +export async function canViewReport(auth: Auth, report: Report) { if (auth.user.isAdmin) { return true; } - if ((auth.user.id = userReport.userId)) { + if ((auth.user.id = report.userId)) { return true; } - if (await canViewWebsite(auth, userReport.websiteId)) { + if (await canViewWebsite(auth, report.websiteId)) { return true; } return false; } -export async function canUpdateUserReport(auth: Auth, userReport: UserReport) { +export async function canUpdateReport(auth: Auth, report: Report) { if (auth.user.isAdmin) { return true; } - if ((auth.user.id = userReport.userId)) { + if ((auth.user.id = report.userId)) { return true; } diff --git a/package.json b/package.json index c4a790dd..bd195662 100644 --- a/package.json +++ b/package.json @@ -91,7 +91,7 @@ "maxmind": "^4.3.6", "moment-timezone": "^0.5.35", "next": "13.3.1", - "next-basics": "^0.27.0", + "next-basics": "^0.30.0", "node-fetch": "^3.2.8", "npm-run-all": "^4.1.5", "react": "^18.2.0", diff --git a/pages/api/reports/[id].ts b/pages/api/reports/[id].ts index 42002d18..d1c6c89a 100644 --- a/pages/api/reports/[id].ts +++ b/pages/api/reports/[id].ts @@ -1,59 +1,68 @@ -import { canUpdateUserReport, canViewUserReport } from 'lib/auth'; +import { canUpdateReport, canViewReport } from 'lib/auth'; import { useAuth, useCors } from 'lib/middleware'; import { NextApiRequestQueryBody } from 'lib/types'; import { NextApiResponse } from 'next'; import { methodNotAllowed, ok, unauthorized } from 'next-basics'; -import { getUserReportById, updateUserReport } from 'queries'; +import { getReportById, updateReport } from 'queries'; -export interface UserReportRequestQuery { +export interface ReportRequestQuery { id: string; } -export interface UserReportRequestBody { +export interface ReportRequestBody { websiteId: string; - reportName: string; - templateName: string; + type: string; + name: string; + description: string; parameters: string; } export default async ( - req: NextApiRequestQueryBody, + req: NextApiRequestQueryBody, res: NextApiResponse, ) => { await useCors(req, res); await useAuth(req, res); if (req.method === 'GET') { - const { id: userReportId } = req.query; + const { id: reportId } = req.query; - const data = await getUserReportById(userReportId); + const data = await getReportById(reportId); - if (!(await canViewUserReport(req.auth, data))) { + if (!(await canViewReport(req.auth, data))) { return unauthorized(res); } + data.parameters = JSON.parse(data.parameters); + return ok(res, data); } if (req.method === 'POST') { - const { id: userReportId } = req.query; + const { id: reportId } = req.query; - const data = await getUserReportById(userReportId); + const { websiteId, type, name, description, parameters } = req.body; - if (!(await canUpdateUserReport(req.auth, data))) { + const data = await getReportById(reportId); + + if (!(await canUpdateReport(req.auth, data))) { return unauthorized(res); } - const updated = await updateUserReport( + const result = await updateReport( { - ...req.body, - }, + websiteId, + type, + name, + description, + parameters: JSON.stringify(parameters), + } as any, { - id: userReportId, + id: reportId, }, ); - return ok(res, updated); + return ok(res, result); } return methodNotAllowed(res); diff --git a/pages/api/reports/index.ts b/pages/api/reports/index.ts index e5855355..55dc4bf5 100644 --- a/pages/api/reports/index.ts +++ b/pages/api/reports/index.ts @@ -3,17 +3,21 @@ import { useAuth, useCors } from 'lib/middleware'; import { NextApiRequestQueryBody } from 'lib/types'; import { NextApiResponse } from 'next'; import { methodNotAllowed, ok } from 'next-basics'; -import { createUserReport, getUserReports } from 'queries'; +import { createReport, getReports } from 'queries'; -export interface UserReportRequestBody { +export interface ReportRequestBody { websiteId: string; - reportName: string; - templateName: string; - parameters: string; + name: string; + type: string; + description: string; + parameters: { + window: string; + urls: string[]; + }; } export default async ( - req: NextApiRequestQueryBody, + req: NextApiRequestQueryBody, res: NextApiResponse, ) => { await useCors(req, res); @@ -24,19 +28,25 @@ export default async ( } = req.auth; if (req.method === 'GET') { - const data = await getUserReports(userId); + const data = await getReports(userId); return ok(res, data); } if (req.method === 'POST') { - const data = await createUserReport({ + const { websiteId, type, name, description, parameters } = req.body; + + const result = await createReport({ id: uuid(), userId, - ...req.body, - }); + websiteId, + type, + name, + description, + parameters: JSON.stringify(parameters), + } as any); - return ok(res, data); + return ok(res, result); } return methodNotAllowed(res); diff --git a/pages/realtime/[id]/index.js b/pages/realtime/[id].js similarity index 100% rename from pages/realtime/[id]/index.js rename to pages/realtime/[id].js diff --git a/pages/reports/[id].js b/pages/reports/[id].js new file mode 100644 index 00000000..95f20a78 --- /dev/null +++ b/pages/reports/[id].js @@ -0,0 +1,20 @@ +import { useRouter } from 'next/router'; +import AppLayout from 'components/layout/AppLayout'; +import FunnelReport from 'components/pages/reports/funnel/FunnelReport'; +import useMessages from 'hooks/useMessages'; + +export default function ReportsPage() { + const { formatMessage, labels } = useMessages(); + const router = useRouter(); + const { id } = router.query; + + if (!id) { + return null; + } + + return ( + + + + ); +} diff --git a/pages/reports/index.js b/pages/reports/index.js index 70f684cb..6da2b943 100644 --- a/pages/reports/index.js +++ b/pages/reports/index.js @@ -1,13 +1,21 @@ +import { useState } from 'react'; +import { Item, Tabs } from 'react-basics'; import AppLayout from 'components/layout/AppLayout'; import ReportList from 'components/pages/reports/ReportList'; import useMessages from 'hooks/useMessages'; export default function ReportsPage() { + const [tab, setTab] = useState('create'); const { formatMessage, labels } = useMessages(); return ( - + + {formatMessage(labels.reports)} + {formatMessage(labels.save)} + + {tab === 'create' && } + {tab === 'saved' &&

My reports

}
); } diff --git a/pages/settings/teams/[id]/index.js b/pages/settings/teams/[id].js similarity index 100% rename from pages/settings/teams/[id]/index.js rename to pages/settings/teams/[id].js diff --git a/pages/settings/users/[id]/index.js b/pages/settings/users/[id].js similarity index 100% rename from pages/settings/users/[id]/index.js rename to pages/settings/users/[id].js diff --git a/pages/settings/websites/[id]/index.js b/pages/settings/websites/[id].js similarity index 100% rename from pages/settings/websites/[id]/index.js rename to pages/settings/websites/[id].js diff --git a/pages/websites/[id]/index.js b/pages/websites/[id].js similarity index 100% rename from pages/websites/[id]/index.js rename to pages/websites/[id].js diff --git a/queries/admin/report.ts b/queries/admin/report.ts new file mode 100644 index 00000000..506902f5 --- /dev/null +++ b/queries/admin/report.ts @@ -0,0 +1,33 @@ +import { Prisma, Report } from '@prisma/client'; +import prisma from 'lib/prisma'; + +export async function createReport(data: Prisma.ReportUncheckedCreateInput): Promise { + return prisma.client.report.create({ data }); +} + +export async function getReportById(reportId: string): Promise { + return prisma.client.report.findUnique({ + where: { + id: reportId, + }, + }); +} + +export async function getReports(userId: string): Promise { + return prisma.client.report.findMany({ + where: { + userId, + }, + }); +} + +export async function updateReport( + data: Prisma.ReportUpdateInput, + where: Prisma.ReportWhereUniqueInput, +): Promise { + return prisma.client.report.update({ data, where }); +} + +export async function deleteReport(where: Prisma.ReportWhereUniqueInput): Promise { + return prisma.client.report.delete({ where }); +} diff --git a/queries/admin/user.ts b/queries/admin/user.ts index b7f452c7..3ba2654b 100644 --- a/queries/admin/user.ts +++ b/queries/admin/user.ts @@ -210,7 +210,7 @@ export async function deleteUser( }, }, }), - client.userReport.deleteMany({ + client.report.deleteMany({ where: { OR: [ { diff --git a/queries/admin/userReport.ts b/queries/admin/userReport.ts deleted file mode 100644 index d31b512e..00000000 --- a/queries/admin/userReport.ts +++ /dev/null @@ -1,37 +0,0 @@ -import { Prisma, UserReport } from '@prisma/client'; -import prisma from 'lib/prisma'; - -export async function createUserReport( - data: Prisma.UserReportUncheckedCreateInput, -): Promise { - return prisma.client.userReport.create({ data }); -} - -export async function getUserReportById(userReportId: string): Promise { - return prisma.client.userReport.findUnique({ - where: { - id: userReportId, - }, - }); -} - -export async function getUserReports(userId: string): Promise { - return prisma.client.userReport.findMany({ - where: { - userId, - }, - }); -} - -export async function updateUserReport( - data: Prisma.UserReportUpdateInput, - where: Prisma.UserReportWhereUniqueInput, -): Promise { - return prisma.client.userReport.update({ data, where }); -} - -export async function deleteUserReport( - where: Prisma.UserReportWhereUniqueInput, -): Promise { - return prisma.client.userReport.delete({ where }); -} diff --git a/queries/admin/website.ts b/queries/admin/website.ts index e6d53fce..8c381d6d 100644 --- a/queries/admin/website.ts +++ b/queries/admin/website.ts @@ -92,7 +92,7 @@ export async function deleteWebsite( websiteId, }, }), - client.userReport.deleteMany({ + client.report.deleteMany({ where: { websiteId, }, diff --git a/queries/index.js b/queries/index.js index e565df25..302c88db 100644 --- a/queries/index.js +++ b/queries/index.js @@ -1,7 +1,7 @@ export * from './admin/team'; export * from './admin/teamUser'; export * from './admin/user'; -export * from './admin/userReport'; +export * from './admin/report'; export * from './admin/website'; export * from './analytics/event/getEventMetrics'; export * from './analytics/event/getEventUsage'; diff --git a/rollup.tracker.config.js b/rollup.tracker.config.js index f4e7223c..827e46f3 100644 --- a/rollup.tracker.config.js +++ b/rollup.tracker.config.js @@ -4,7 +4,7 @@ import replace from '@rollup/plugin-replace'; import { terser } from 'rollup-plugin-terser'; export default { - input: 'tracker/index.js', + input: 'tracker/[id].js', output: { file: 'public/script.js', format: 'iife', diff --git a/store/reports.js b/store/reports.js deleted file mode 100644 index fa658d4d..00000000 --- a/store/reports.js +++ /dev/null @@ -1,57 +0,0 @@ -import { create } from 'zustand'; -import produce from 'immer'; -import { getRandomChars } from 'next-basics'; - -const emptyReport = { - name: 'Untitled', - description: '', - parameters: {}, -}; - -const initialState = {}; - -const store = create(() => ({ ...initialState })); - -export function updateReport(id, data) { - const report = store.getState()[id]; - - if (report) { - store.setState( - produce(state => { - const item = state[id]; - const { parameters, ...rest } = data; - - if (parameters) { - item.parameters = { ...item.parameters, ...parameters }; - } - - for (const key in rest) { - item[key] = rest[key]; - } - - return state; - }), - ); - } -} - -export function createReport(parameters) { - const id = `new_${getRandomChars(16)}`; - const report = { ...emptyReport, id, parameters }; - - store.setState( - produce(state => { - state[id] = report; - - return state; - }), - ); - - return report; -} - -export default store; - -if (typeof window !== 'undefined') { - window.__STORE__ = store; -} diff --git a/yarn.lock b/yarn.lock index 1d0bc7fd..e2c1432d 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3715,11 +3715,6 @@ balanced-match@^2.0.0: resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-2.0.0.tgz#dc70f920d78db8b858535795867bf48f820633d9" integrity sha512-1ugUSr8BHXRnK23KfuYS+gVMC3LB8QGH9W1iGtDPsNWoQbgtXSExkBu2aDR4epiGWZOjZsj6lDl/N/AqqTC3UA== -base-x@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/base-x/-/base-x-4.0.0.tgz#d0e3b7753450c73f8ad2389b5c018a4af7b2224a" - integrity sha512-FuwxlW4H5kh37X/oW59pwTzzTKRzfrrQwhmyspRM7swOEZcHtDZSCt45U6oKgtuFE+WYPblePMVIPR4RZrh/hw== - base64-js@^1.3.1: version "1.5.1" resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a" @@ -7019,14 +7014,14 @@ natural-compare@^1.4.0: resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7" integrity sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw== -next-basics@^0.27.0: - version "0.27.0" - resolved "https://registry.yarnpkg.com/next-basics/-/next-basics-0.27.0.tgz#7d46c88de4b53cadfef86230f8cbe7dba6f10dc6" - integrity sha512-ZviF4O4/14eBjGG7fK83oswuM/rur37TRxcjXCKPJN4kOTUgrzn9Sz+vpzkB8PZ2WaHv5ONQ7TkBEwnhMCEyMQ== +next-basics@^0.30.0: + version "0.30.0" + resolved "https://registry.yarnpkg.com/next-basics/-/next-basics-0.30.0.tgz#1d5f1b3bd3cd66218f00ab0390659a819ed274fa" + integrity sha512-IWNrBsxM8watkvWHWkUp5PC0WypqeHMEaj5rCufjEfwuIe++ePtDrYgUKY6Se6y6QJWbyHliozCpa1KpVyZHnQ== dependencies: - base-x "^4.0.0" bcryptjs "^2.4.3" jsonwebtoken "^9.0.0" + pure-rand "^6.0.2" next@13.3.1: version "13.3.1" @@ -8144,6 +8139,11 @@ punycode@^2.1.0: resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.3.0.tgz#f67fa67c94da8f4d0cfff981aee4118064199b8f" integrity sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA== +pure-rand@^6.0.2: + version "6.0.2" + resolved "https://registry.yarnpkg.com/pure-rand/-/pure-rand-6.0.2.tgz#a9c2ddcae9b68d736a8163036f088a2781c8b306" + integrity sha512-6Yg0ekpKICSjPswYOuC5sku/TSWaRYlA0qsXqJgM/d/4pLPHPuTxK7Nbf7jFKzAeedUhR8C7K9Uv63FBsSo8xQ== + qs@~6.5.2: version "6.5.3" resolved "https://registry.npmjs.org/qs/-/qs-6.5.3.tgz"