diff --git a/components/input/DateFilter.js b/components/input/DateFilter.js
index ecdf9039..4d60627d 100644
--- a/components/input/DateFilter.js
+++ b/components/input/DateFilter.js
@@ -9,7 +9,7 @@ import useApi from 'hooks/useApi';
import useDateRange from 'hooks/useDateRange';
import useMessages from 'hooks/useMessages';
-export function DateFilter({ websiteId, value, className, onChange, isForm, alignment }) {
+export function DateFilter({ websiteId, value, className, onChange, alignment }) {
const { formatMessage, labels } = useMessages();
const { get } = useApi();
const [dateRange, setDateRange] = useDateRange(websiteId);
@@ -23,7 +23,7 @@ export function DateFilter({ websiteId, value, className, onChange, isForm, alig
if (data) {
const websiteRange = { value, ...getDateRangeValues(new Date(data.createdAt), Date.now()) };
- if (!isForm) {
+ if (!onChange) {
setDateRange(websiteRange);
}
@@ -32,15 +32,13 @@ export function DateFilter({ websiteId, value, className, onChange, isForm, alig
}
}
} else if (value !== 'all') {
- if (!isForm) {
+ if (!onChange) {
setDateRange(value);
}
if (onChange) {
onChange(value);
}
-
- console.log(value);
}
}
diff --git a/components/layout/SettingsLayout.js b/components/layout/SettingsLayout.js
index c79f0909..d58154ca 100644
--- a/components/layout/SettingsLayout.js
+++ b/components/layout/SettingsLayout.js
@@ -15,6 +15,7 @@ export function SettingsLayout({ children }) {
const items = [
{ key: 'websites', label: formatMessage(labels.websites), url: '/settings/websites' },
{ key: 'teams', label: formatMessage(labels.teams), url: '/settings/teams' },
+ { key: 'reports', label: 'Reports', url: '/settings/reports/funnel' },
user.isAdmin && { key: 'users', label: formatMessage(labels.users), url: '/settings/users' },
{ key: 'profile', label: formatMessage(labels.profile), url: '/settings/profile' },
].filter(n => n);
diff --git a/components/messages.js b/components/messages.js
index 245e8591..fe4b833a 100644
--- a/components/messages.js
+++ b/components/messages.js
@@ -18,7 +18,9 @@ export const labels = defineMessages({
admin: { id: 'label.admin', defaultMessage: 'Administrator' },
confirm: { id: 'label.confirm', defaultMessage: 'Confirm' },
details: { id: 'label.details', defaultMessage: 'Details' },
+ website: { id: 'label.website', defaultMessage: 'Website' },
websites: { id: 'label.websites', defaultMessage: 'Websites' },
+ reports: { id: 'label.reports', defaultMessage: 'Reports' },
created: { id: 'label.created', defaultMessage: 'Created' },
edit: { id: 'label.edit', defaultMessage: 'Edit' },
name: { id: 'label.name', defaultMessage: 'Name' },
@@ -183,6 +185,10 @@ export const messages = defineMessages({
id: 'message.delete-website-warning',
defaultMessage: 'All website data will be deleted.',
},
+ noResultsFound: {
+ id: 'messages.no-results-found',
+ defaultMessage: 'No results were found.',
+ },
noWebsitesConfigured: {
id: 'messages.no-websites-configured',
defaultMessage: 'You do not have any websites configured.',
diff --git a/components/pages/reports/FunnelChart.js b/components/pages/reports/FunnelChart.js
index 44c99092..8739e1da 100644
--- a/components/pages/reports/FunnelChart.js
+++ b/components/pages/reports/FunnelChart.js
@@ -1,43 +1,40 @@
import FunnelGraph from 'funnel-graph-js/dist/js/funnel-graph';
import { useEffect, useRef } from 'react';
+import EmptyPlaceholder from 'components/common/EmptyPlaceholder';
+import useMessages from 'hooks/useMessages';
-export default function FunnelChart() {
+export default function FunnelChart({ data }) {
+ const { formatMessage, labels, messages } = useMessages();
const funnel = useRef(null);
useEffect(() => {
- funnel.current.innerHTML = '';
+ if (data && data.length > 0) {
+ funnel.current.innerHTML = '';
- const data = {
- labels: ['Cv Sent', '1st Interview', '2nd Interview', '3rd Interview', 'Offer'],
- subLabels: ['Cv Sent', '1st Interview', '2nd Interview', '3rd Interview', 'Offer'],
- colors: [
- ['#FFB178', '#FF78B1', '#FF3C8E'],
- ['#FFB178', '#FF78B1', '#FF3C8E'],
- ['#A0BBFF', '#EC77FF'],
- ['#A0F9FF', '#7795FF'],
- ['#FFB178', '#FF78B1', '#FF3C8E'],
- ],
- values: [[3500], [3300], [2000], [600], [330]],
- };
+ const chartData = {
+ labels: data.map(a => a.url),
+ colors: ['#147af3', '#e0f2ff'],
+ values: data.map(a => a.count),
+ };
- const graph = new FunnelGraph({
- container: '.funnel',
- gradientDirection: 'horizontal',
- data: data,
- displayPercent: true,
- direction: 'Vertical',
- width: 1000,
- height: 350,
- subLabelValue: 'values',
- });
+ const graph = new FunnelGraph({
+ container: '.funnel',
+ gradientDirection: 'horizontal',
+ data: chartData,
+ displayPercent: true,
+ direction: 'Vertical',
+ width: 1000,
+ height: 350,
+ });
- graph.draw();
- }, []);
+ graph.draw();
+ }
+ }, [data]);
return (
-
+ <>
+ {data?.length > 0 && }
+ {data?.length === 0 && }
+ >
);
}
diff --git a/components/pages/reports/FunnelForm.js b/components/pages/reports/FunnelForm.js
index 081105f1..56be6732 100644
--- a/components/pages/reports/FunnelForm.js
+++ b/components/pages/reports/FunnelForm.js
@@ -1,12 +1,8 @@
-import { useMutation } from '@tanstack/react-query';
import DateFilter from 'components/input/DateFilter';
import WebsiteSelect from 'components/input/WebsiteSelect';
-import useApi from 'hooks/useApi';
import useMessages from 'hooks/useMessages';
-import useUser from 'hooks/useUser';
import { parseDateRange } from 'lib/date';
-import { useRouter } from 'next/router';
-import { useEffect, useState } from 'react';
+import { useState } from 'react';
import {
Button,
Form,
@@ -17,15 +13,15 @@ import {
TextField,
} from 'react-basics';
import styles from './FunnelForm.module.css';
-import { getNextInternalQuery } from 'next/dist/server/request-meta';
export function FunnelForm({ onSearch }) {
- const { formatMessage, labels, getMessage } = useMessages();
- const [dateRange, setDateRange] = useState(null);
- const [startDate, setStartDate] = useState(null);
- const [endDate, setEndDate] = useState(null);
+ const { formatMessage, labels } = useMessages();
+ const [dateRange, setDateRange] = useState('');
+ const [startAt, setStartAt] = useState();
+ const [endAt, setEndAt] = useState();
const [urls, setUrls] = useState(['']);
const [websiteId, setWebsiteId] = useState('');
+ const [window, setWindow] = useState(60);
const handleSubmit = async data => {
onSearch(data);
@@ -35,14 +31,16 @@ export function FunnelForm({ onSearch }) {
const { startDate, endDate } = parseDateRange(value);
setDateRange(value);
- setStartDate(startDate);
- setEndDate(endDate);
+ setStartAt(startDate.getTime());
+ setEndAt(endDate.getTime());
};
- const handleAddUrl = () => setUrls([...urls, 'meow']);
+ const handleAddUrl = () => setUrls([...urls, '']);
const handleRemoveUrl = i => setUrls(urls.splice(i, 1));
+ const handleWindowChange = value => setWindow(value.target.value);
+
const handleUrlChange = (value, i) => {
const nextUrls = [...urls];
@@ -55,13 +53,14 @@ export function FunnelForm({ onSearch }) {
>
diff --git a/components/pages/reports/FunnelForm.module.css b/components/pages/reports/FunnelForm.module.css
index 9a8d924b..2706a99a 100644
--- a/components/pages/reports/FunnelForm.module.css
+++ b/components/pages/reports/FunnelForm.module.css
@@ -17,3 +17,12 @@
min-height: 0px;
max-height: 0px;
}
+
+.urlFormRow {
+ flex-direction: row;
+ gap: 0em;
+}
+
+.urlFormRow label {
+ min-width: 80px;
+}
diff --git a/components/pages/reports/FunnelPage.js b/components/pages/reports/FunnelPage.js
index 3cfa63a8..c715d857 100644
--- a/components/pages/reports/FunnelPage.js
+++ b/components/pages/reports/FunnelPage.js
@@ -1,26 +1,38 @@
+import { useMutation } from '@tanstack/react-query';
import Page from 'components/layout/Page';
+import PageHeader from 'components/layout/PageHeader';
+import useApi from 'hooks/useApi';
+import { useState } from 'react';
import FunnelChart from './FunnelChart';
+import FunnelTable from './FunnelTable';
import FunnelForm from './FunnelForm';
+import styles from './FunnelPage.module.css';
export default function FunnelPage() {
- function handleOnSearch() {
+ const { post } = useApi();
+ const { mutate, error, isLoading } = useMutation(data => post('/reports/funnel', data));
+ const [data, setData] = useState();
+
+ function handleOnSearch(data) {
// do API CALL to api/reports/funnel to get funnelData
// Get DATA
+ mutate(data, {
+ onSuccess: async data => {
+ setData(data);
+ },
+ });
}
return (
- funnelPage
+
+
+
{/* */}
- website / start/endDate urls: []
-
- {/* {!chartLoaded && }
- {chartLoaded && (
- <>
- {!view && }
- {view && }
- >
- )} */}
+
+
Filters
+
+
);
}
diff --git a/components/pages/reports/FunnelPage.module.css b/components/pages/reports/FunnelPage.module.css
new file mode 100644
index 00000000..aed66b74
--- /dev/null
+++ b/components/pages/reports/FunnelPage.module.css
@@ -0,0 +1,10 @@
+.filters {
+ display: flex;
+ flex-direction: column;
+ justify-content: space-between;
+ border: 1px solid var(--base400);
+ border-radius: var(--border-radius);
+ line-height: 32px;
+ padding: 10px;
+ overflow: hidden;
+}
diff --git a/components/pages/reports/FunnelTable.js b/components/pages/reports/FunnelTable.js
new file mode 100644
index 00000000..fa40fd13
--- /dev/null
+++ b/components/pages/reports/FunnelTable.js
@@ -0,0 +1,17 @@
+import DataTable from 'components/metrics/DataTable';
+import useMessages from 'hooks/useMessages';
+import { useState } from 'react';
+
+export function DevicesTable({ ...props }) {
+ const { formatMessage, labels } = useMessages();
+ const { data } = props;
+
+ const tableData =
+ data?.map(a => ({ x: a.url, y: a.count, z: (a.count / data[0].count) * 100 })) || [];
+
+ console.log(tableData);
+
+ return ;
+}
+
+export default DevicesTable;
diff --git a/db/postgresql/schema.prisma b/db/postgresql/schema.prisma
index ee5ff4b4..318d455d 100644
--- a/db/postgresql/schema.prisma
+++ b/db/postgresql/schema.prisma
@@ -17,9 +17,9 @@ model User {
updatedAt DateTime? @updatedAt @map("updated_at") @db.Timestamptz(6)
deletedAt DateTime? @map("deleted_at") @db.Timestamptz(6)
- website Website[]
- teamUser TeamUser[]
- ReportTemplate UserReport[]
+ website Website[]
+ teamUser TeamUser[]
+ Report Report[]
@@map("user")
}
@@ -60,6 +60,7 @@ model Website {
user User? @relation(fields: [userId], references: [id])
teamWebsite TeamWebsite[]
eventData EventData[]
+ Report Report[]
@@index([userId])
@@index([createdAt])
@@ -156,18 +157,21 @@ model TeamWebsite {
@@map("team_website")
}
-model UserReport {
- id String @id() @unique() @map("report_id") @db.Uuid
- userId String @map("user_id") @db.Uuid
- websiteId String @map("website_id") @db.Uuid
- reportName String @map("report_name") @db.VarChar(200)
- templateName String @map("template_name") @db.VarChar(200)
- parameters String @map("parameters") @db.VarChar(6000)
- createdAt DateTime? @default(now()) @map("created_at") @db.Timestamptz(6)
- updatedAt DateTime? @updatedAt @map("updated_at") @db.Timestamptz(6)
+model Report {
+ id String @id() @unique() @map("report_id") @db.Uuid
+ userId String @map("user_id") @db.Uuid
+ websiteId String @map("website_id") @db.Uuid
+ type String @map("type") @db.VarChar(200)
+ name String @map("name") @db.VarChar(200)
+ description String @map("description") @db.VarChar(500)
+ parameters String @map("parameters") @db.VarChar(6000)
+ createdAt DateTime? @default(now()) @map("created_at") @db.Timestamptz(6)
+ updatedAt DateTime? @updatedAt @map("updated_at") @db.Timestamptz(6)
- user User @relation(fields: [userId], references: [id])
+ user User @relation(fields: [userId], references: [id])
+ website Website @relation(fields: [websiteId], references: [id])
@@index([userId])
- @@map("user_report")
+ @@index([websiteId])
+ @@map("report")
}
diff --git a/pages/api/reports/funnel.ts b/pages/api/reports/funnel.ts
index ee450eb6..6e3eb602 100644
--- a/pages/api/reports/funnel.ts
+++ b/pages/api/reports/funnel.ts
@@ -40,7 +40,7 @@ export default async (
startDate,
endDate,
urls,
- windowMinutes: window,
+ windowMinutes: +window,
});
return ok(res, data);
diff --git a/pages/settings/reports/funnel.js b/pages/settings/reports/funnel.js
new file mode 100644
index 00000000..d8d7a5b8
--- /dev/null
+++ b/pages/settings/reports/funnel.js
@@ -0,0 +1,16 @@
+import AppLayout from 'components/layout/AppLayout';
+import SettingsLayout from 'components/layout/SettingsLayout';
+import FunnelPage from 'components/pages/reports/FunnelPage';
+import useMessages from 'hooks/useMessages';
+
+export default function DetailsPage() {
+ const { formatMessage, labels } = useMessages();
+
+ return (
+
+
+
+
+
+ );
+}
diff --git a/pages/reports/funnel.js b/pages/settings/reports/index.js
similarity index 76%
rename from pages/reports/funnel.js
rename to pages/settings/reports/index.js
index d4bf7dd2..ce0a3726 100644
--- a/pages/reports/funnel.js
+++ b/pages/settings/reports/index.js
@@ -2,6 +2,7 @@ import { useRouter } from 'next/router';
import AppLayout from 'components/layout/AppLayout';
import FunnelPage from 'components/pages/reports/FunnelPage';
import useMessages from 'hooks/useMessages';
+import SettingsLayout from 'components/layout/SettingsLayout';
export default function DetailsPage() {
// const { formatMessage, labels } = useMessages();
@@ -15,8 +16,10 @@ export default function DetailsPage() {
// return {/* */};
return (
-
-
-
+
+
+
+
+
);
}