This commit is contained in:
Florian Herrengt 2023-08-04 17:45:47 +01:00
commit ef14ffb1f1
42 changed files with 48711 additions and 0 deletions

BIN
.DS_Store vendored Normal file

Binary file not shown.

1
.env Normal file
View File

@ -0,0 +1 @@
BASE_URL=https://app.nocodelytics.com

4
.gitignore vendored Normal file
View File

@ -0,0 +1,4 @@
node_modules
bundle.zip
dist
public

30
.storybook/main.ts Normal file
View File

@ -0,0 +1,30 @@
import { StorybookConfig } from "@storybook/react-vite";
import { mergeConfig } from "vite";
export default {
stories: ["../src/**/*.mdx", "../src/**/*.stories.@(js|jsx|mjs|ts|tsx)"],
addons: [
"@storybook/addon-links",
"@storybook/addon-essentials",
"@storybook/addon-onboarding",
"@storybook/addon-interactions",
"@storybook/addon-backgrounds",
],
core: {
builder: "@storybook/builder-vite",
},
async viteFinal(config) {
return mergeConfig(config, {
optimizeDeps: {
include: ["storybook-dark-mode"],
},
});
},
framework: {
name: "@storybook/react-vite",
options: {},
},
docs: {
autodocs: "tag",
},
} as StorybookConfig;

View File

@ -0,0 +1,7 @@
<style>
html,
body,
#storybook-root {
height: 100%;
}
</style>

22
.storybook/preview.ts Normal file
View File

@ -0,0 +1,22 @@
// import "../src/App/App.scss";
import { Parameters } from "@storybook/addons";
export const parameters: Parameters = {
actions: { argTypesRegex: "^on[A-Z].*" },
options: {},
controls: {
matchers: {
color: /(background|color)$/i,
date: /Date$/,
},
},
backgrounds: {
default: "dark",
values: [
{
name: "dark",
value: "#404040",
},
],
},
};

19
README.md Normal file
View File

@ -0,0 +1,19 @@
# Webflow Designer Extension
Display views and visitors for the current page.
![demo](./demo.gif)
## Developing
```bash
npm run dev
```
## Deploying
```bash
npm run build
```
Upload `bundle.zip`

14
codegen.yml Normal file
View File

@ -0,0 +1,14 @@
overwrite: true
schema: "http://localhost:8000/api/graphql"
documents: "**/src/**/*.graphql"
generates:
src/generated/graphql.ts:
plugins:
- "typescript"
- "typescript-operations"
- typescript-react-apollo
config:
maybeValue: T | null | undefined
src/generated/apolloHelpers.ts:
plugins:
- typescript-apollo-client-helpers

BIN
demo.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 MiB

12
index.html Normal file
View File

@ -0,0 +1,12 @@
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Inter" />
<style>
body {
font-family: "Inter", sans-serif;
font-size: 12px;
background-color: #2b2b2b;
}
</style>
<div id="root"></div>
<!-- <script src="https://d3e54v103j8qbb.cloudfront.net/gen/js/entrypoint-designer-extensions-client.e4ef1682e412a922c7e4.js"></script> -->
<script type="module" src="src/main.tsx"></script>

46393
package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

53
package.json Normal file
View File

@ -0,0 +1,53 @@
{
"name": "nocodelytics-webflow-designer-extension",
"version": "1.0.0",
"description": "This is an example Webflow Designer extension written in TypeScript to get you started. Check out our [documentation](https://docs.developers.webflow.com/v2.0.0/docs/create-a-designer-extensions) for in-depth information about Designer Extension features and API.",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"storybook": "storybook dev -p 6006",
"build-storybook": "storybook build",
"dev": "vite",
"build": "vite build && webflow extension bundle",
"codegen": "graphql-codegen --config codegen.yml"
},
"keywords": [],
"author": "",
"license": "ISC",
"dependencies": {
"@apollo/client": "^3.7.17",
"@graphql-codegen/cli": "^5.0.0",
"@graphql-codegen/typescript-apollo-client-helpers": "^2.2.6",
"@graphql-codegen/typescript-operations": "^4.0.1",
"@graphql-codegen/typescript-react-apollo": "^3.3.7",
"@storybook/builder-vite": "^7.1.0",
"@vitejs/plugin-react-refresh": "^1.3.6",
"@webflow/designer-extension-typings": "^0.1.3",
"chart.js": "^4.3.0",
"chartjs-adapter-date-fns": "^3.0.0",
"dotenv": "^16.3.1",
"react": "^18.2.0",
"react-chartjs-2": "^5.2.0",
"react-dom": "^18.2.0",
"react-router-dom": "^6.14.2",
"vite": "^4.4.4"
},
"devDependencies": {
"@storybook/addon-actions": "^7.1.0",
"@storybook/addon-backgrounds": "^7.1.0",
"@storybook/addon-essentials": "^7.1.0",
"@storybook/addon-interactions": "^7.1.0",
"@storybook/addon-links": "^7.1.0",
"@storybook/addon-onboarding": "^1.0.7",
"@storybook/blocks": "^7.1.0",
"@storybook/react": "^7.1.0",
"@storybook/react-vite": "^7.1.0",
"@storybook/testing-library": "^0.2.0",
"@storybook/types": "^7.1.0",
"@types/node": "^20.4.2",
"@types/react-dom": "^18.2.7",
"prop-types": "^15.8.1",
"storybook": "^7.1.0",
"storybook-builder-vite": "^0.1.23"
}
}

14
src/App.tsx Normal file
View File

@ -0,0 +1,14 @@
import { ApolloProvider } from "@apollo/client";
import React from "react";
import { apolloClient } from "./apolloClient";
import { AuthIFrame } from "./components/AuthIFrame";
import { MetricDetailsPage } from "./pages";
export const App: React.FC = () => {
return (
<ApolloProvider client={apolloClient}>
<AuthIFrame />
<MetricDetailsPage />
</ApolloProvider>
);
};

43
src/apolloClient.ts Normal file
View File

@ -0,0 +1,43 @@
import {
ApolloClient,
InMemoryCache,
ServerParseError,
split,
} from "@apollo/client";
import { BatchHttpLink as HttpLink } from "@apollo/client/link/batch-http";
import { setContext } from "@apollo/client/link/context";
import { onError } from "@apollo/client/link/error";
import { getMainDefinition } from "@apollo/client/utilities";
import { config } from "./config";
const errorLink = onError(({ networkError }) => {
if (networkError) {
const { statusCode } = networkError as ServerParseError;
if (statusCode === 401) {
localStorage.removeItem(config.localstorageKeys.token);
}
}
});
const authLink = setContext((_, { headers }) => {
const token = localStorage.getItem(config.localstorageKeys.token);
console.log(token);
return {
headers: {
...headers,
authorization: token ? `Bearer ${token}` : "",
},
};
});
const httpLink = new HttpLink({
uri: `${config.api.url}/graphql`,
});
export const apolloClient = new ApolloClient({
uri: `${config.api.url}/graphql`,
link: authLink.concat(errorLink).concat(httpLink),
// connectToDevTools: true,
cache: new InMemoryCache(),
});

View File

@ -0,0 +1,45 @@
import React, { useEffect, useState } from "react";
import { config } from "../config";
/**
* This is based on Rodney Urquhart's example https://github.com/RodneyU215
* https://github.com/Webflow-Examples/Apps-v2-Hybrid-Auth-Demo/tree/main#key-components
* https://www.loom.com/share/28afbb72a30e42ca9333a77e65fbd025
*
* tl;dr app.nocodelytics.com/webflow_auth.html will post the token with
* `window.parent.postMessage(token, extensionURL)`
*
* This component listens to that event and save the token in local storage
*/
export const AuthIFrame: React.FC = () => {
const [url, setUrl] = useState<string>();
const [done, setDone] = useState<boolean>(false);
const handleMessage = (event: MessageEvent) => {
if (event.origin.startsWith(config.baseUrl)) {
localStorage.setItem(config.localstorageKeys.token, event.data);
console.info(
`new token received from ${config.authIFrameUrl}`,
event.data
);
window.removeEventListener("message", handleMessage);
setUrl("");
setDone(true);
} else {
return;
}
};
useEffect(() => {
window.addEventListener("message", handleMessage);
setUrl(`${config.authIFrameUrl}`);
return () => {
window.removeEventListener("message", handleMessage);
};
}, []);
if (done) {
return null;
}
return url ? <iframe style={{}} src={url} /> : null;
};

View File

@ -0,0 +1,14 @@
import { Button } from "./Button";
// More on how to set up stories at: https://storybook.js.org/docs/react/writing-stories/introduction#default-export
export default {
title: "Example/Button",
component: Button,
};
// More on writing stories with args: https://storybook.js.org/docs/react/writing-stories/args
export const Primary = {
args: {
children: "Click me",
},
};

View File

@ -0,0 +1,20 @@
import React from "react";
interface ButtonProps {}
export const Button: React.FC<React.PropsWithChildren<ButtonProps>> = (
props
) => (
<button
style={{
backgroundColor: "#1280EE",
color: "white",
borderColor: "#363636",
border: 1,
padding: "4px 16px",
fontSize: 11,
}}
>
{props.children}
</button>
);

View File

@ -0,0 +1 @@
export * from "./Button";

View File

@ -0,0 +1,16 @@
import { LineChart, LineChartProps } from "./LineChart";
export default {
title: "LineChart",
component: LineChart,
};
export const Primary = {
args: {
title: "Views",
data: {
labels: ["Jan", "Feb"],
values: [1, 2],
},
} as LineChartProps,
};

View File

@ -0,0 +1,104 @@
import React from "react";
import {
Chart as ChartJS,
CategoryScale,
LinearScale,
PointElement,
LineElement,
Title,
Tooltip,
TimeScale,
} from "chart.js";
import { Line } from "react-chartjs-2";
import "chartjs-adapter-date-fns";
ChartJS.register(
CategoryScale,
LinearScale,
PointElement,
LineElement,
Title,
Tooltip,
TimeScale
);
ChartJS.defaults.color = "#D9D9D9";
export interface LineChartProps {
title: string;
data: {
labels: string[];
values: number[];
};
}
/**
* This component is a temporary
* We should be using @nocodelytics/components instead but it's not ready yet
* This works for the first version
*/
export const LineChart: React.FC<LineChartProps> = (props) => {
return (
<div
style={{
position: "relative",
height: "100%",
backgroundColor: "#404040",
padding: "10px",
border: "1px solid #212121",
}}
>
<Line
style={{ height: "100%", width: "100%" }}
options={{
responsive: true,
maintainAspectRatio: false,
interaction: { mode: "nearest", intersect: false },
scales: {
y: { ticks: { stepSize: 1 } },
x: {
type: "time",
},
},
plugins: {
title: {
display: Boolean(props.title),
text: props.title,
},
tooltip: {
animation: false,
backgroundColor: "#2b2b2b",
bodyColor: "white",
displayColors: false,
mode: "nearest",
caretSize: 0,
borderWidth: 1,
borderColor: "#212121",
cornerRadius: 0,
xAlign: "center",
yAlign: "center",
callbacks: {
title: () => "",
label: (item) => {
const date = new Date(item.parsed.x).toLocaleDateString();
return `${date}: ${item.parsed.y}`;
},
},
},
},
}}
data={{
labels: props.data.labels,
datasets: [
{
pointStyle: "line",
data: props.data.values,
borderColor: "#0073E6",
backgroundColor: "#0073E6",
},
],
}}
/>
</div>
);
};

View File

@ -0,0 +1 @@
export * from "./LineChart";

View File

@ -0,0 +1,24 @@
import { Dropdown, DropdownProps } from "./Dropdown";
export default {
title: "Dropdown",
component: Dropdown,
};
export const Primary = {
args: {
options: [
{ label: "last 30 days", value: "30" },
{ label: "last 7 days", value: "7" },
],
} as DropdownProps,
};
export const Open = {
args: {
options: [
{ label: "last 30 days", value: "30" },
{ label: "last 7 days", value: "7" },
],
open: true,
} as DropdownProps,
};

View File

@ -0,0 +1,97 @@
import React, { useEffect, useRef, useState } from "react";
import { theme } from "../../theme";
export interface DropdownProps {
onChange: (value: string) => void;
options: Array<{ label: string; value: string }>;
selected?: string;
open?: boolean;
}
const DropdownOptions: React.FC<DropdownProps> = (props) => {
return (
<div style={{}}>
{props.options.map(({ label, value }) => (
<div
style={{ display: "flex", padding: "4px 8px", cursor: "pointer" }}
onClick={() => {
props.onChange(value);
}}
>
<div style={{ marginRight: 10, width: 10 }}>
{props.selected === value ? "X" : ""}
</div>
<div>{label}</div>
</div>
))}
</div>
);
};
/**
* Temp comp until @nocodelytics/components is ready
* We'll use <Timeframe /> which will also include types
*/
export const Dropdown: React.FC<DropdownProps> = (props) => {
if (!props.options.length) {
throw new Error(`Dropdown requires options`);
}
const [isOpen, setIsOpen] = useState<boolean>(props.open || false);
const [selected, setSelected] = useState<string>(
props.selected || props.options[0].value
);
const ref = useRef<HTMLDivElement>(null);
useEffect(() => {
function handleClickOutside(event: Event) {
if (ref.current && !ref.current.contains(event.target as Node)) {
setIsOpen(false);
}
}
document.addEventListener("mousedown", handleClickOutside);
return () => {
document.removeEventListener("mousedown", handleClickOutside);
};
}, [ref]);
return (
<div
ref={ref}
onClick={() => setIsOpen(!isOpen)}
style={{
position: "relative",
...theme.text.primary,
...theme.background.secondary,
...theme.border.primary,
cursor: "pointer",
maxWidth: 150,
}}
>
<div
style={{
padding: "0px 4px 0px 8px",
height: 24,
justifyContent: "unset",
gridAutoFlow: "column",
display: "grid",
alignItems: "center",
gap: 2,
}}
>
{props.options.find(({ value }) => selected === value)!.label}
</div>
{isOpen && (
<DropdownOptions
{...props}
selected={selected}
onChange={(value) => {
setIsOpen(false);
setSelected(value);
props.onChange(value);
}}
/>
)}
</div>
);
};

View File

@ -0,0 +1 @@
export * from "./Dropdown";

3
src/components/index.tsx Normal file
View File

@ -0,0 +1,3 @@
export * from "./Button";
export * from "./Chart";
export * from "./Dropdown";

25
src/config.ts Normal file
View File

@ -0,0 +1,25 @@
interface Config {
baseUrl: string;
api: { url: string };
authIFrameUrl: string;
localstorageKeys: {
token: string;
};
webflow: {
appId: string;
};
}
const baseUrl = process.env.BASE_URL || "https://app.nocodelytics.com";
export const config: Config = {
baseUrl,
authIFrameUrl: `${baseUrl}/webflow-auth-iframe`,
api: { url: baseUrl + "/api" },
localstorageKeys: {
token: process.env.LOCALSTORAGE_KEY_TOKEN || "nocodelytics:token", // allows you to easily switch between env or users for debugging
},
webflow: {
appId: "64cd1cc2aa1df4ca56c397eb",
},
};

60
src/containers/Chart.tsx Normal file
View File

@ -0,0 +1,60 @@
import React, { useEffect, useState } from "react";
import { LineChart } from "../components";
import {
ChartDataQueryVariables,
useChartDataQuery,
} from "../generated/graphql";
import { useCurrentSiteInfo } from "../hooks";
interface ChartProps {
input: ChartDataQueryVariables["input"];
}
export const Chart: React.FC<ChartProps> = (props) => {
const currentSiteInfo = useCurrentSiteInfo();
const chartDataQueryResults = useChartDataQuery({
variables: {
input: {
siteId: currentSiteInfo.data.nocodelyticsId,
...props.input,
},
},
skip: !currentSiteInfo.data.nocodelyticsId,
});
const loading: boolean =
chartDataQueryResults.loading || currentSiteInfo.loading;
const error: string | undefined =
chartDataQueryResults.error?.message || currentSiteInfo.error;
const data = chartDataQueryResults.data;
if (loading) {
return <div>Loading</div>;
}
if (error) {
return <div style={{ color: "red" }}>{error}</div>;
}
if (!data?.chartData) {
return <div>No data</div>;
}
const total = data?.chartData.data[0].values.reduce(
(acc, item) => acc + item.value,
0
);
return (
<div>
<div style={{ height: 175, width: "100%" }}>
<LineChart
title={`${props.input.metricType} (${total})`}
data={data?.chartData.data[0].values.reduce(
(acc, item) => {
acc.labels.push(new Date(Date.parse(item.label)));
acc.values.push(item.value);
return acc;
},
{ labels: [], values: [] }
)}
/>
</div>
</div>
);
};

View File

@ -0,0 +1,486 @@
import { FieldPolicy, FieldReadFunction, TypePolicies, TypePolicy } from '@apollo/client/cache';
export type AdminReportKeySpecifier = ('users' | AdminReportKeySpecifier)[];
export type AdminReportFieldPolicy = {
users?: FieldPolicy<any> | FieldReadFunction<any>
};
export type CheckScriptHeaderResultsKeySpecifier = ('errorMessage' | 'found' | 'headerContent' | CheckScriptHeaderResultsKeySpecifier)[];
export type CheckScriptHeaderResultsFieldPolicy = {
errorMessage?: FieldPolicy<any> | FieldReadFunction<any>,
found?: FieldPolicy<any> | FieldReadFunction<any>,
headerContent?: FieldPolicy<any> | FieldReadFunction<any>
};
export type CmsMetricValueKeySpecifier = ('name' | 'value' | 'webflowItemId' | CmsMetricValueKeySpecifier)[];
export type CmsMetricValueFieldPolicy = {
name?: FieldPolicy<any> | FieldReadFunction<any>,
value?: FieldPolicy<any> | FieldReadFunction<any>,
webflowItemId?: FieldPolicy<any> | FieldReadFunction<any>
};
export type DashboardKeySpecifier = ('createdAt' | 'gridLayout' | 'id' | 'isPublic' | 'name' | 'organisationId' | 'siteId' | 'updatedAt' | DashboardKeySpecifier)[];
export type DashboardFieldPolicy = {
createdAt?: FieldPolicy<any> | FieldReadFunction<any>,
gridLayout?: FieldPolicy<any> | FieldReadFunction<any>,
id?: FieldPolicy<any> | FieldReadFunction<any>,
isPublic?: FieldPolicy<any> | FieldReadFunction<any>,
name?: FieldPolicy<any> | FieldReadFunction<any>,
organisationId?: FieldPolicy<any> | FieldReadFunction<any>,
siteId?: FieldPolicy<any> | FieldReadFunction<any>,
updatedAt?: FieldPolicy<any> | FieldReadFunction<any>
};
export type DashboardGridLayoutItemKeySpecifier = ('h' | 'id' | 'w' | 'x' | 'y' | DashboardGridLayoutItemKeySpecifier)[];
export type DashboardGridLayoutItemFieldPolicy = {
h?: FieldPolicy<any> | FieldReadFunction<any>,
id?: FieldPolicy<any> | FieldReadFunction<any>,
w?: FieldPolicy<any> | FieldReadFunction<any>,
x?: FieldPolicy<any> | FieldReadFunction<any>,
y?: FieldPolicy<any> | FieldReadFunction<any>
};
export type FeaturesKeySpecifier = ('memberFilter' | FeaturesKeySpecifier)[];
export type FeaturesFieldPolicy = {
memberFilter?: FieldPolicy<any> | FieldReadFunction<any>
};
export type MeKeySpecifier = ('organisations' | 'user' | MeKeySpecifier)[];
export type MeFieldPolicy = {
organisations?: FieldPolicy<any> | FieldReadFunction<any>,
user?: FieldPolicy<any> | FieldReadFunction<any>
};
export type MeOrganisationKeySpecifier = ('id' | MeOrganisationKeySpecifier)[];
export type MeOrganisationFieldPolicy = {
id?: FieldPolicy<any> | FieldReadFunction<any>
};
export type MetricKeySpecifier = ('breakdownBy' | 'chartData' | 'cssSelector' | 'dashboardId' | 'id' | 'metricType' | 'name' | 'path' | 'uniqueVisitor' | 'webflowCollectionId' | MetricKeySpecifier)[];
export type MetricFieldPolicy = {
breakdownBy?: FieldPolicy<any> | FieldReadFunction<any>,
chartData?: FieldPolicy<any> | FieldReadFunction<any>,
cssSelector?: FieldPolicy<any> | FieldReadFunction<any>,
dashboardId?: FieldPolicy<any> | FieldReadFunction<any>,
id?: FieldPolicy<any> | FieldReadFunction<any>,
metricType?: FieldPolicy<any> | FieldReadFunction<any>,
name?: FieldPolicy<any> | FieldReadFunction<any>,
path?: FieldPolicy<any> | FieldReadFunction<any>,
uniqueVisitor?: FieldPolicy<any> | FieldReadFunction<any>,
webflowCollectionId?: FieldPolicy<any> | FieldReadFunction<any>
};
export type MetricByValueKeySpecifier = ('name' | 'value' | MetricByValueKeySpecifier)[];
export type MetricByValueFieldPolicy = {
name?: FieldPolicy<any> | FieldReadFunction<any>,
value?: FieldPolicy<any> | FieldReadFunction<any>
};
export type MetricChartKeySpecifier = ('data' | 'delta' | 'fromCache' | 'lastSync' | 'name' | 'queryTime' | 'total' | MetricChartKeySpecifier)[];
export type MetricChartFieldPolicy = {
data?: FieldPolicy<any> | FieldReadFunction<any>,
delta?: FieldPolicy<any> | FieldReadFunction<any>,
fromCache?: FieldPolicy<any> | FieldReadFunction<any>,
lastSync?: FieldPolicy<any> | FieldReadFunction<any>,
name?: FieldPolicy<any> | FieldReadFunction<any>,
queryTime?: FieldPolicy<any> | FieldReadFunction<any>,
total?: FieldPolicy<any> | FieldReadFunction<any>
};
export type MetricChartDataKeySpecifier = ('name' | 'values' | MetricChartDataKeySpecifier)[];
export type MetricChartDataFieldPolicy = {
name?: FieldPolicy<any> | FieldReadFunction<any>,
values?: FieldPolicy<any> | FieldReadFunction<any>
};
export type MetricChartDataValuesKeySpecifier = ('label' | 'value' | MetricChartDataValuesKeySpecifier)[];
export type MetricChartDataValuesFieldPolicy = {
label?: FieldPolicy<any> | FieldReadFunction<any>,
value?: FieldPolicy<any> | FieldReadFunction<any>
};
export type MetricEventKeySpecifier = ('cmsCollectionItemSlug' | 'cmsCollectionSlug' | 'cmsWebflowCollectionId' | 'cmsWebflowItemId' | 'createdAt' | 'cssClass' | 'cssId' | 'currentUrl' | 'deviceBrand' | 'deviceClientEngine' | 'deviceClientEngineVersion' | 'deviceClientName' | 'deviceClientType' | 'deviceClientVersion' | 'deviceId' | 'deviceModel' | 'deviceOsName' | 'deviceOsPlatform' | 'deviceOsVersion' | 'deviceType' | 'deviceUserAgent' | 'domain' | 'eventType' | 'formDisplayedAt' | 'formLastInteractionAt' | 'formSubmittedAt' | 'geoIpCity' | 'geoIpCountry' | 'geoIpEu' | 'geoIpLatitude' | 'geoIpLongitude' | 'geoIpRegion' | 'geoIpTimezone' | 'id' | 'ipAddress' | 'path' | 'referer' | 'siteId' | 'updatedAt' | 'value' | 'visitorId' | MetricEventKeySpecifier)[];
export type MetricEventFieldPolicy = {
cmsCollectionItemSlug?: FieldPolicy<any> | FieldReadFunction<any>,
cmsCollectionSlug?: FieldPolicy<any> | FieldReadFunction<any>,
cmsWebflowCollectionId?: FieldPolicy<any> | FieldReadFunction<any>,
cmsWebflowItemId?: FieldPolicy<any> | FieldReadFunction<any>,
createdAt?: FieldPolicy<any> | FieldReadFunction<any>,
cssClass?: FieldPolicy<any> | FieldReadFunction<any>,
cssId?: FieldPolicy<any> | FieldReadFunction<any>,
currentUrl?: FieldPolicy<any> | FieldReadFunction<any>,
deviceBrand?: FieldPolicy<any> | FieldReadFunction<any>,
deviceClientEngine?: FieldPolicy<any> | FieldReadFunction<any>,
deviceClientEngineVersion?: FieldPolicy<any> | FieldReadFunction<any>,
deviceClientName?: FieldPolicy<any> | FieldReadFunction<any>,
deviceClientType?: FieldPolicy<any> | FieldReadFunction<any>,
deviceClientVersion?: FieldPolicy<any> | FieldReadFunction<any>,
deviceId?: FieldPolicy<any> | FieldReadFunction<any>,
deviceModel?: FieldPolicy<any> | FieldReadFunction<any>,
deviceOsName?: FieldPolicy<any> | FieldReadFunction<any>,
deviceOsPlatform?: FieldPolicy<any> | FieldReadFunction<any>,
deviceOsVersion?: FieldPolicy<any> | FieldReadFunction<any>,
deviceType?: FieldPolicy<any> | FieldReadFunction<any>,
deviceUserAgent?: FieldPolicy<any> | FieldReadFunction<any>,
domain?: FieldPolicy<any> | FieldReadFunction<any>,
eventType?: FieldPolicy<any> | FieldReadFunction<any>,
formDisplayedAt?: FieldPolicy<any> | FieldReadFunction<any>,
formLastInteractionAt?: FieldPolicy<any> | FieldReadFunction<any>,
formSubmittedAt?: FieldPolicy<any> | FieldReadFunction<any>,
geoIpCity?: FieldPolicy<any> | FieldReadFunction<any>,
geoIpCountry?: FieldPolicy<any> | FieldReadFunction<any>,
geoIpEu?: FieldPolicy<any> | FieldReadFunction<any>,
geoIpLatitude?: FieldPolicy<any> | FieldReadFunction<any>,
geoIpLongitude?: FieldPolicy<any> | FieldReadFunction<any>,
geoIpRegion?: FieldPolicy<any> | FieldReadFunction<any>,
geoIpTimezone?: FieldPolicy<any> | FieldReadFunction<any>,
id?: FieldPolicy<any> | FieldReadFunction<any>,
ipAddress?: FieldPolicy<any> | FieldReadFunction<any>,
path?: FieldPolicy<any> | FieldReadFunction<any>,
referer?: FieldPolicy<any> | FieldReadFunction<any>,
siteId?: FieldPolicy<any> | FieldReadFunction<any>,
updatedAt?: FieldPolicy<any> | FieldReadFunction<any>,
value?: FieldPolicy<any> | FieldReadFunction<any>,
visitorId?: FieldPolicy<any> | FieldReadFunction<any>
};
export type MetricValueKeySpecifier = ('date' | 'value' | MetricValueKeySpecifier)[];
export type MetricValueFieldPolicy = {
date?: FieldPolicy<any> | FieldReadFunction<any>,
value?: FieldPolicy<any> | FieldReadFunction<any>
};
export type MetricValueResultsKeySpecifier = ('label' | 'values' | MetricValueResultsKeySpecifier)[];
export type MetricValueResultsFieldPolicy = {
label?: FieldPolicy<any> | FieldReadFunction<any>,
values?: FieldPolicy<any> | FieldReadFunction<any>
};
export type MutationKeySpecifier = ('checkScriptHeader' | 'createDashboard' | 'createMetric' | 'createToken' | 'deleteDashboard' | 'deleteMetric' | 'deleteSite' | 'findOrUpdateVisitor' | 'renewToken' | 'reportError' | 'resetCacheBySiteId' | 'sendMagicLink' | 'signIn' | 'signUpWithWebflow' | 'syncSiteWithWebflow' | 'syncWebflowSites' | 'updateDashboard' | 'updateMetric' | 'updateSiteById' | 'updateSiteGridLayout' | MutationKeySpecifier)[];
export type MutationFieldPolicy = {
checkScriptHeader?: FieldPolicy<any> | FieldReadFunction<any>,
createDashboard?: FieldPolicy<any> | FieldReadFunction<any>,
createMetric?: FieldPolicy<any> | FieldReadFunction<any>,
createToken?: FieldPolicy<any> | FieldReadFunction<any>,
deleteDashboard?: FieldPolicy<any> | FieldReadFunction<any>,
deleteMetric?: FieldPolicy<any> | FieldReadFunction<any>,
deleteSite?: FieldPolicy<any> | FieldReadFunction<any>,
findOrUpdateVisitor?: FieldPolicy<any> | FieldReadFunction<any>,
renewToken?: FieldPolicy<any> | FieldReadFunction<any>,
reportError?: FieldPolicy<any> | FieldReadFunction<any>,
resetCacheBySiteId?: FieldPolicy<any> | FieldReadFunction<any>,
sendMagicLink?: FieldPolicy<any> | FieldReadFunction<any>,
signIn?: FieldPolicy<any> | FieldReadFunction<any>,
signUpWithWebflow?: FieldPolicy<any> | FieldReadFunction<any>,
syncSiteWithWebflow?: FieldPolicy<any> | FieldReadFunction<any>,
syncWebflowSites?: FieldPolicy<any> | FieldReadFunction<any>,
updateDashboard?: FieldPolicy<any> | FieldReadFunction<any>,
updateMetric?: FieldPolicy<any> | FieldReadFunction<any>,
updateSiteById?: FieldPolicy<any> | FieldReadFunction<any>,
updateSiteGridLayout?: FieldPolicy<any> | FieldReadFunction<any>
};
export type OrganisationKeySpecifier = ('features' | 'id' | OrganisationKeySpecifier)[];
export type OrganisationFieldPolicy = {
features?: FieldPolicy<any> | FieldReadFunction<any>,
id?: FieldPolicy<any> | FieldReadFunction<any>
};
export type PageInfoKeySpecifier = ('hasNextPage' | PageInfoKeySpecifier)[];
export type PageInfoFieldPolicy = {
hasNextPage?: FieldPolicy<any> | FieldReadFunction<any>
};
export type PaginationKeySpecifier = ('hasMore' | 'total' | PaginationKeySpecifier)[];
export type PaginationFieldPolicy = {
hasMore?: FieldPolicy<any> | FieldReadFunction<any>,
total?: FieldPolicy<any> | FieldReadFunction<any>
};
export type QueryKeySpecifier = ('adminReport' | 'chartData' | 'dashboard' | 'dashboardBySiteId' | 'eventTypes' | 'features' | 'maintenance' | 'me' | 'metric' | 'metricByValues' | 'metricTypes' | 'metricValues' | 'metricsByDashboardId' | 'metricsBySiteId' | 'organisation' | 'queueEvents' | 'site' | 'siteById' | 'siteByWebflowId' | 'siteHtmlElements' | 'sites' | 'stripeCustomerPortalLink' | 'users' | 'visitor' | 'visitors' | QueryKeySpecifier)[];
export type QueryFieldPolicy = {
adminReport?: FieldPolicy<any> | FieldReadFunction<any>,
chartData?: FieldPolicy<any> | FieldReadFunction<any>,
dashboard?: FieldPolicy<any> | FieldReadFunction<any>,
dashboardBySiteId?: FieldPolicy<any> | FieldReadFunction<any>,
eventTypes?: FieldPolicy<any> | FieldReadFunction<any>,
features?: FieldPolicy<any> | FieldReadFunction<any>,
maintenance?: FieldPolicy<any> | FieldReadFunction<any>,
me?: FieldPolicy<any> | FieldReadFunction<any>,
metric?: FieldPolicy<any> | FieldReadFunction<any>,
metricByValues?: FieldPolicy<any> | FieldReadFunction<any>,
metricTypes?: FieldPolicy<any> | FieldReadFunction<any>,
metricValues?: FieldPolicy<any> | FieldReadFunction<any>,
metricsByDashboardId?: FieldPolicy<any> | FieldReadFunction<any>,
metricsBySiteId?: FieldPolicy<any> | FieldReadFunction<any>,
organisation?: FieldPolicy<any> | FieldReadFunction<any>,
queueEvents?: FieldPolicy<any> | FieldReadFunction<any>,
site?: FieldPolicy<any> | FieldReadFunction<any>,
siteById?: FieldPolicy<any> | FieldReadFunction<any>,
siteByWebflowId?: FieldPolicy<any> | FieldReadFunction<any>,
siteHtmlElements?: FieldPolicy<any> | FieldReadFunction<any>,
sites?: FieldPolicy<any> | FieldReadFunction<any>,
stripeCustomerPortalLink?: FieldPolicy<any> | FieldReadFunction<any>,
users?: FieldPolicy<any> | FieldReadFunction<any>,
visitor?: FieldPolicy<any> | FieldReadFunction<any>,
visitors?: FieldPolicy<any> | FieldReadFunction<any>
};
export type QueueEventKeySpecifier = ('cmsItemIndex' | 'cmsItemSlug' | 'cmsListIndex' | 'createdAt' | 'cssClass' | 'cssId' | 'domain' | 'eventType' | 'formStage' | 'id' | 'ip' | 'path' | 'referer' | 'siteId' | 'userAgent' | 'value' | 'visitorId' | QueueEventKeySpecifier)[];
export type QueueEventFieldPolicy = {
cmsItemIndex?: FieldPolicy<any> | FieldReadFunction<any>,
cmsItemSlug?: FieldPolicy<any> | FieldReadFunction<any>,
cmsListIndex?: FieldPolicy<any> | FieldReadFunction<any>,
createdAt?: FieldPolicy<any> | FieldReadFunction<any>,
cssClass?: FieldPolicy<any> | FieldReadFunction<any>,
cssId?: FieldPolicy<any> | FieldReadFunction<any>,
domain?: FieldPolicy<any> | FieldReadFunction<any>,
eventType?: FieldPolicy<any> | FieldReadFunction<any>,
formStage?: FieldPolicy<any> | FieldReadFunction<any>,
id?: FieldPolicy<any> | FieldReadFunction<any>,
ip?: FieldPolicy<any> | FieldReadFunction<any>,
path?: FieldPolicy<any> | FieldReadFunction<any>,
referer?: FieldPolicy<any> | FieldReadFunction<any>,
siteId?: FieldPolicy<any> | FieldReadFunction<any>,
userAgent?: FieldPolicy<any> | FieldReadFunction<any>,
value?: FieldPolicy<any> | FieldReadFunction<any>,
visitorId?: FieldPolicy<any> | FieldReadFunction<any>
};
export type SiteKeySpecifier = ('collections' | 'createdAt' | 'dashboards' | 'domains' | 'eventSources' | 'gridLayout' | 'id' | 'isEnabled' | 'isPublic' | 'metrics' | 'name' | 'organisationId' | 'previewUrl' | 'shortName' | 'totalMetricsCount' | 'updatedAt' | 'webflowId' | SiteKeySpecifier)[];
export type SiteFieldPolicy = {
collections?: FieldPolicy<any> | FieldReadFunction<any>,
createdAt?: FieldPolicy<any> | FieldReadFunction<any>,
dashboards?: FieldPolicy<any> | FieldReadFunction<any>,
domains?: FieldPolicy<any> | FieldReadFunction<any>,
eventSources?: FieldPolicy<any> | FieldReadFunction<any>,
gridLayout?: FieldPolicy<any> | FieldReadFunction<any>,
id?: FieldPolicy<any> | FieldReadFunction<any>,
isEnabled?: FieldPolicy<any> | FieldReadFunction<any>,
isPublic?: FieldPolicy<any> | FieldReadFunction<any>,
metrics?: FieldPolicy<any> | FieldReadFunction<any>,
name?: FieldPolicy<any> | FieldReadFunction<any>,
organisationId?: FieldPolicy<any> | FieldReadFunction<any>,
previewUrl?: FieldPolicy<any> | FieldReadFunction<any>,
shortName?: FieldPolicy<any> | FieldReadFunction<any>,
totalMetricsCount?: FieldPolicy<any> | FieldReadFunction<any>,
updatedAt?: FieldPolicy<any> | FieldReadFunction<any>,
webflowId?: FieldPolicy<any> | FieldReadFunction<any>
};
export type SiteEdgeKeySpecifier = ('cursor' | 'node' | SiteEdgeKeySpecifier)[];
export type SiteEdgeFieldPolicy = {
cursor?: FieldPolicy<any> | FieldReadFunction<any>,
node?: FieldPolicy<any> | FieldReadFunction<any>
};
export type SiteGridLayoutItemKeySpecifier = ('h' | 'id' | 'w' | 'x' | 'y' | SiteGridLayoutItemKeySpecifier)[];
export type SiteGridLayoutItemFieldPolicy = {
h?: FieldPolicy<any> | FieldReadFunction<any>,
id?: FieldPolicy<any> | FieldReadFunction<any>,
w?: FieldPolicy<any> | FieldReadFunction<any>,
x?: FieldPolicy<any> | FieldReadFunction<any>,
y?: FieldPolicy<any> | FieldReadFunction<any>
};
export type SiteHtmlElementKeySpecifier = ('html' | 'id' | 'text' | SiteHtmlElementKeySpecifier)[];
export type SiteHtmlElementFieldPolicy = {
html?: FieldPolicy<any> | FieldReadFunction<any>,
id?: FieldPolicy<any> | FieldReadFunction<any>,
text?: FieldPolicy<any> | FieldReadFunction<any>
};
export type SitePaginatedKeySpecifier = ('edges' | 'pageInfo' | 'totalCount' | SitePaginatedKeySpecifier)[];
export type SitePaginatedFieldPolicy = {
edges?: FieldPolicy<any> | FieldReadFunction<any>,
pageInfo?: FieldPolicy<any> | FieldReadFunction<any>,
totalCount?: FieldPolicy<any> | FieldReadFunction<any>
};
export type UserKeySpecifier = ('createdAt' | 'email' | 'firstname' | 'id' | 'lastname' | UserKeySpecifier)[];
export type UserFieldPolicy = {
createdAt?: FieldPolicy<any> | FieldReadFunction<any>,
email?: FieldPolicy<any> | FieldReadFunction<any>,
firstname?: FieldPolicy<any> | FieldReadFunction<any>,
id?: FieldPolicy<any> | FieldReadFunction<any>,
lastname?: FieldPolicy<any> | FieldReadFunction<any>
};
export type UserEdgeKeySpecifier = ('cursor' | 'node' | UserEdgeKeySpecifier)[];
export type UserEdgeFieldPolicy = {
cursor?: FieldPolicy<any> | FieldReadFunction<any>,
node?: FieldPolicy<any> | FieldReadFunction<any>
};
export type UserPaginatedKeySpecifier = ('edges' | 'pageInfo' | 'totalCount' | UserPaginatedKeySpecifier)[];
export type UserPaginatedFieldPolicy = {
edges?: FieldPolicy<any> | FieldReadFunction<any>,
pageInfo?: FieldPolicy<any> | FieldReadFunction<any>,
totalCount?: FieldPolicy<any> | FieldReadFunction<any>
};
export type VisitorKeySpecifier = ('city' | 'country' | 'device' | 'emailAddress' | 'events' | 'id' | 'initialReferer' | 'lastInteraction' | VisitorKeySpecifier)[];
export type VisitorFieldPolicy = {
city?: FieldPolicy<any> | FieldReadFunction<any>,
country?: FieldPolicy<any> | FieldReadFunction<any>,
device?: FieldPolicy<any> | FieldReadFunction<any>,
emailAddress?: FieldPolicy<any> | FieldReadFunction<any>,
events?: FieldPolicy<any> | FieldReadFunction<any>,
id?: FieldPolicy<any> | FieldReadFunction<any>,
initialReferer?: FieldPolicy<any> | FieldReadFunction<any>,
lastInteraction?: FieldPolicy<any> | FieldReadFunction<any>
};
export type VisitorEdgeKeySpecifier = ('cursor' | 'node' | VisitorEdgeKeySpecifier)[];
export type VisitorEdgeFieldPolicy = {
cursor?: FieldPolicy<any> | FieldReadFunction<any>,
node?: FieldPolicy<any> | FieldReadFunction<any>
};
export type VisitorPaginatedKeySpecifier = ('edges' | 'pageInfo' | 'totalCount' | VisitorPaginatedKeySpecifier)[];
export type VisitorPaginatedFieldPolicy = {
edges?: FieldPolicy<any> | FieldReadFunction<any>,
pageInfo?: FieldPolicy<any> | FieldReadFunction<any>,
totalCount?: FieldPolicy<any> | FieldReadFunction<any>
};
export type WebflowCollectionKeySpecifier = ('fields' | 'id' | 'items' | 'name' | WebflowCollectionKeySpecifier)[];
export type WebflowCollectionFieldPolicy = {
fields?: FieldPolicy<any> | FieldReadFunction<any>,
id?: FieldPolicy<any> | FieldReadFunction<any>,
items?: FieldPolicy<any> | FieldReadFunction<any>,
name?: FieldPolicy<any> | FieldReadFunction<any>
};
export type WebflowCollectionFieldsKeySpecifier = ('id' | 'name' | 'slug' | 'type' | WebflowCollectionFieldsKeySpecifier)[];
export type WebflowCollectionFieldsFieldPolicy = {
id?: FieldPolicy<any> | FieldReadFunction<any>,
name?: FieldPolicy<any> | FieldReadFunction<any>,
slug?: FieldPolicy<any> | FieldReadFunction<any>,
type?: FieldPolicy<any> | FieldReadFunction<any>
};
export type WebflowCollectionItemsKeySpecifier = ('id' | 'name' | WebflowCollectionItemsKeySpecifier)[];
export type WebflowCollectionItemsFieldPolicy = {
id?: FieldPolicy<any> | FieldReadFunction<any>,
name?: FieldPolicy<any> | FieldReadFunction<any>
};
export type WebflowSitesKeySpecifier = ('name' | WebflowSitesKeySpecifier)[];
export type WebflowSitesFieldPolicy = {
name?: FieldPolicy<any> | FieldReadFunction<any>
};
export type StrictTypedTypePolicies = {
AdminReport?: Omit<TypePolicy, "fields" | "keyFields"> & {
keyFields?: false | AdminReportKeySpecifier | (() => undefined | AdminReportKeySpecifier),
fields?: AdminReportFieldPolicy,
},
CheckScriptHeaderResults?: Omit<TypePolicy, "fields" | "keyFields"> & {
keyFields?: false | CheckScriptHeaderResultsKeySpecifier | (() => undefined | CheckScriptHeaderResultsKeySpecifier),
fields?: CheckScriptHeaderResultsFieldPolicy,
},
CmsMetricValue?: Omit<TypePolicy, "fields" | "keyFields"> & {
keyFields?: false | CmsMetricValueKeySpecifier | (() => undefined | CmsMetricValueKeySpecifier),
fields?: CmsMetricValueFieldPolicy,
},
Dashboard?: Omit<TypePolicy, "fields" | "keyFields"> & {
keyFields?: false | DashboardKeySpecifier | (() => undefined | DashboardKeySpecifier),
fields?: DashboardFieldPolicy,
},
DashboardGridLayoutItem?: Omit<TypePolicy, "fields" | "keyFields"> & {
keyFields?: false | DashboardGridLayoutItemKeySpecifier | (() => undefined | DashboardGridLayoutItemKeySpecifier),
fields?: DashboardGridLayoutItemFieldPolicy,
},
Features?: Omit<TypePolicy, "fields" | "keyFields"> & {
keyFields?: false | FeaturesKeySpecifier | (() => undefined | FeaturesKeySpecifier),
fields?: FeaturesFieldPolicy,
},
Me?: Omit<TypePolicy, "fields" | "keyFields"> & {
keyFields?: false | MeKeySpecifier | (() => undefined | MeKeySpecifier),
fields?: MeFieldPolicy,
},
MeOrganisation?: Omit<TypePolicy, "fields" | "keyFields"> & {
keyFields?: false | MeOrganisationKeySpecifier | (() => undefined | MeOrganisationKeySpecifier),
fields?: MeOrganisationFieldPolicy,
},
Metric?: Omit<TypePolicy, "fields" | "keyFields"> & {
keyFields?: false | MetricKeySpecifier | (() => undefined | MetricKeySpecifier),
fields?: MetricFieldPolicy,
},
MetricByValue?: Omit<TypePolicy, "fields" | "keyFields"> & {
keyFields?: false | MetricByValueKeySpecifier | (() => undefined | MetricByValueKeySpecifier),
fields?: MetricByValueFieldPolicy,
},
MetricChart?: Omit<TypePolicy, "fields" | "keyFields"> & {
keyFields?: false | MetricChartKeySpecifier | (() => undefined | MetricChartKeySpecifier),
fields?: MetricChartFieldPolicy,
},
MetricChartData?: Omit<TypePolicy, "fields" | "keyFields"> & {
keyFields?: false | MetricChartDataKeySpecifier | (() => undefined | MetricChartDataKeySpecifier),
fields?: MetricChartDataFieldPolicy,
},
MetricChartDataValues?: Omit<TypePolicy, "fields" | "keyFields"> & {
keyFields?: false | MetricChartDataValuesKeySpecifier | (() => undefined | MetricChartDataValuesKeySpecifier),
fields?: MetricChartDataValuesFieldPolicy,
},
MetricEvent?: Omit<TypePolicy, "fields" | "keyFields"> & {
keyFields?: false | MetricEventKeySpecifier | (() => undefined | MetricEventKeySpecifier),
fields?: MetricEventFieldPolicy,
},
MetricValue?: Omit<TypePolicy, "fields" | "keyFields"> & {
keyFields?: false | MetricValueKeySpecifier | (() => undefined | MetricValueKeySpecifier),
fields?: MetricValueFieldPolicy,
},
MetricValueResults?: Omit<TypePolicy, "fields" | "keyFields"> & {
keyFields?: false | MetricValueResultsKeySpecifier | (() => undefined | MetricValueResultsKeySpecifier),
fields?: MetricValueResultsFieldPolicy,
},
Mutation?: Omit<TypePolicy, "fields" | "keyFields"> & {
keyFields?: false | MutationKeySpecifier | (() => undefined | MutationKeySpecifier),
fields?: MutationFieldPolicy,
},
Organisation?: Omit<TypePolicy, "fields" | "keyFields"> & {
keyFields?: false | OrganisationKeySpecifier | (() => undefined | OrganisationKeySpecifier),
fields?: OrganisationFieldPolicy,
},
PageInfo?: Omit<TypePolicy, "fields" | "keyFields"> & {
keyFields?: false | PageInfoKeySpecifier | (() => undefined | PageInfoKeySpecifier),
fields?: PageInfoFieldPolicy,
},
Pagination?: Omit<TypePolicy, "fields" | "keyFields"> & {
keyFields?: false | PaginationKeySpecifier | (() => undefined | PaginationKeySpecifier),
fields?: PaginationFieldPolicy,
},
Query?: Omit<TypePolicy, "fields" | "keyFields"> & {
keyFields?: false | QueryKeySpecifier | (() => undefined | QueryKeySpecifier),
fields?: QueryFieldPolicy,
},
QueueEvent?: Omit<TypePolicy, "fields" | "keyFields"> & {
keyFields?: false | QueueEventKeySpecifier | (() => undefined | QueueEventKeySpecifier),
fields?: QueueEventFieldPolicy,
},
Site?: Omit<TypePolicy, "fields" | "keyFields"> & {
keyFields?: false | SiteKeySpecifier | (() => undefined | SiteKeySpecifier),
fields?: SiteFieldPolicy,
},
SiteEdge?: Omit<TypePolicy, "fields" | "keyFields"> & {
keyFields?: false | SiteEdgeKeySpecifier | (() => undefined | SiteEdgeKeySpecifier),
fields?: SiteEdgeFieldPolicy,
},
SiteGridLayoutItem?: Omit<TypePolicy, "fields" | "keyFields"> & {
keyFields?: false | SiteGridLayoutItemKeySpecifier | (() => undefined | SiteGridLayoutItemKeySpecifier),
fields?: SiteGridLayoutItemFieldPolicy,
},
SiteHtmlElement?: Omit<TypePolicy, "fields" | "keyFields"> & {
keyFields?: false | SiteHtmlElementKeySpecifier | (() => undefined | SiteHtmlElementKeySpecifier),
fields?: SiteHtmlElementFieldPolicy,
},
SitePaginated?: Omit<TypePolicy, "fields" | "keyFields"> & {
keyFields?: false | SitePaginatedKeySpecifier | (() => undefined | SitePaginatedKeySpecifier),
fields?: SitePaginatedFieldPolicy,
},
User?: Omit<TypePolicy, "fields" | "keyFields"> & {
keyFields?: false | UserKeySpecifier | (() => undefined | UserKeySpecifier),
fields?: UserFieldPolicy,
},
UserEdge?: Omit<TypePolicy, "fields" | "keyFields"> & {
keyFields?: false | UserEdgeKeySpecifier | (() => undefined | UserEdgeKeySpecifier),
fields?: UserEdgeFieldPolicy,
},
UserPaginated?: Omit<TypePolicy, "fields" | "keyFields"> & {
keyFields?: false | UserPaginatedKeySpecifier | (() => undefined | UserPaginatedKeySpecifier),
fields?: UserPaginatedFieldPolicy,
},
Visitor?: Omit<TypePolicy, "fields" | "keyFields"> & {
keyFields?: false | VisitorKeySpecifier | (() => undefined | VisitorKeySpecifier),
fields?: VisitorFieldPolicy,
},
VisitorEdge?: Omit<TypePolicy, "fields" | "keyFields"> & {
keyFields?: false | VisitorEdgeKeySpecifier | (() => undefined | VisitorEdgeKeySpecifier),
fields?: VisitorEdgeFieldPolicy,
},
VisitorPaginated?: Omit<TypePolicy, "fields" | "keyFields"> & {
keyFields?: false | VisitorPaginatedKeySpecifier | (() => undefined | VisitorPaginatedKeySpecifier),
fields?: VisitorPaginatedFieldPolicy,
},
WebflowCollection?: Omit<TypePolicy, "fields" | "keyFields"> & {
keyFields?: false | WebflowCollectionKeySpecifier | (() => undefined | WebflowCollectionKeySpecifier),
fields?: WebflowCollectionFieldPolicy,
},
WebflowCollectionFields?: Omit<TypePolicy, "fields" | "keyFields"> & {
keyFields?: false | WebflowCollectionFieldsKeySpecifier | (() => undefined | WebflowCollectionFieldsKeySpecifier),
fields?: WebflowCollectionFieldsFieldPolicy,
},
WebflowCollectionItems?: Omit<TypePolicy, "fields" | "keyFields"> & {
keyFields?: false | WebflowCollectionItemsKeySpecifier | (() => undefined | WebflowCollectionItemsKeySpecifier),
fields?: WebflowCollectionItemsFieldPolicy,
},
WebflowSites?: Omit<TypePolicy, "fields" | "keyFields"> & {
keyFields?: false | WebflowSitesKeySpecifier | (() => undefined | WebflowSitesKeySpecifier),
fields?: WebflowSitesFieldPolicy,
}
};
export type TypedTypePolicies = StrictTypedTypePolicies & TypePolicies;

943
src/generated/graphql.ts Normal file
View File

@ -0,0 +1,943 @@
import { gql } from '@apollo/client';
import * as Apollo from '@apollo/client';
export type Maybe<T> = T | null | undefined;
export type InputMaybe<T> = T | null | undefined;
export type Exact<T extends { [key: string]: unknown }> = { [K in keyof T]: T[K] };
export type MakeOptional<T, K extends keyof T> = Omit<T, K> & { [SubKey in K]?: Maybe<T[SubKey]> };
export type MakeMaybe<T, K extends keyof T> = Omit<T, K> & { [SubKey in K]: Maybe<T[SubKey]> };
export type MakeEmpty<T extends { [key: string]: unknown }, K extends keyof T> = { [_ in K]?: never };
export type Incremental<T> = T | { [P in keyof T]?: P extends ' $fragmentName' | '__typename' ? T[P] : never };
const defaultOptions = {} as const;
/** All built-in and custom scalars, mapped to their actual values */
export type Scalars = {
ID: { input: string; output: string; }
String: { input: string; output: string; }
Boolean: { input: boolean; output: boolean; }
Int: { input: number; output: number; }
Float: { input: number; output: number; }
BigInt: { input: any; output: any; }
Byte: { input: any; output: any; }
Currency: { input: any; output: any; }
Date: { input: any; output: any; }
DateTime: { input: any; output: any; }
Duration: { input: any; output: any; }
EmailAddress: { input: any; output: any; }
GUID: { input: any; output: any; }
HSL: { input: any; output: any; }
HSLA: { input: any; output: any; }
HexColorCode: { input: any; output: any; }
Hexadecimal: { input: any; output: any; }
IBAN: { input: any; output: any; }
IPv4: { input: any; output: any; }
IPv6: { input: any; output: any; }
ISBN: { input: any; output: any; }
ISO8601Duration: { input: any; output: any; }
JSON: { input: any; output: any; }
JSONObject: { input: any; output: any; }
JWT: { input: any; output: any; }
Latitude: { input: any; output: any; }
LocalDate: { input: any; output: any; }
LocalEndTime: { input: any; output: any; }
LocalTime: { input: any; output: any; }
Long: { input: any; output: any; }
Longitude: { input: any; output: any; }
MAC: { input: any; output: any; }
NegativeFloat: { input: any; output: any; }
NegativeInt: { input: any; output: any; }
NonEmptyString: { input: any; output: any; }
NonNegativeFloat: { input: any; output: any; }
NonNegativeInt: { input: any; output: any; }
NonPositiveFloat: { input: any; output: any; }
NonPositiveInt: { input: any; output: any; }
ObjectID: { input: any; output: any; }
PhoneNumber: { input: any; output: any; }
Port: { input: any; output: any; }
PositiveFloat: { input: any; output: any; }
PositiveInt: { input: any; output: any; }
PostalCode: { input: any; output: any; }
RGB: { input: any; output: any; }
RGBA: { input: any; output: any; }
SafeInt: { input: any; output: any; }
Time: { input: any; output: any; }
Timestamp: { input: any; output: any; }
URL: { input: any; output: any; }
USCurrency: { input: any; output: any; }
UUID: { input: any; output: any; }
UnsignedFloat: { input: any; output: any; }
UnsignedInt: { input: any; output: any; }
UtcOffset: { input: any; output: any; }
Void: { input: any; output: any; }
};
export type AdminReport = {
__typename?: 'AdminReport';
users: Array<User>;
};
export type CheckScriptHeaderResults = {
__typename?: 'CheckScriptHeaderResults';
errorMessage?: Maybe<Scalars['String']['output']>;
found: Scalars['Boolean']['output'];
headerContent: Scalars['String']['output'];
};
export type CmsMetricValue = {
__typename?: 'CmsMetricValue';
name: Scalars['String']['output'];
value: Scalars['PositiveInt']['output'];
webflowItemId: Scalars['String']['output'];
};
export type CreateDashboardInput = {
name: Scalars['String']['input'];
siteId: Scalars['String']['input'];
};
export type CreateMetricEventInput = {
cssClass?: InputMaybe<Scalars['String']['input']>;
cssId?: InputMaybe<Scalars['String']['input']>;
eventType: EventType;
id?: InputMaybe<Scalars['UUID']['input']>;
locationHref: Scalars['String']['input'];
referer?: InputMaybe<Scalars['String']['input']>;
siteId: Scalars['String']['input'];
value?: InputMaybe<Scalars['String']['input']>;
visitor: VisitorInput;
};
export type CreateMetricInput = {
breakdownBy?: InputMaybe<MetricChartBreakdownBy>;
cssSelector?: InputMaybe<Scalars['String']['input']>;
dashboardId: Scalars['String']['input'];
metricType: MetricType;
name: Scalars['String']['input'];
path?: InputMaybe<Scalars['String']['input']>;
uniqueVisitor?: InputMaybe<Scalars['Boolean']['input']>;
webflowCollectionId?: InputMaybe<Scalars['String']['input']>;
};
export type CreateSiteGridLayoutItemInput = {
h: Scalars['PositiveInt']['input'];
id: Scalars['String']['input'];
w: Scalars['PositiveInt']['input'];
x: Scalars['PositiveInt']['input'];
y: Scalars['PositiveInt']['input'];
};
export type Dashboard = {
__typename?: 'Dashboard';
createdAt: Scalars['Date']['output'];
gridLayout?: Maybe<Array<SiteGridLayoutItem>>;
id: Scalars['String']['output'];
isPublic: Scalars['Boolean']['output'];
name: Scalars['String']['output'];
organisationId: Scalars['String']['output'];
siteId: Scalars['String']['output'];
updatedAt: Scalars['Date']['output'];
};
export type DashboardGridLayoutItem = {
__typename?: 'DashboardGridLayoutItem';
h: Scalars['PositiveInt']['output'];
id: Scalars['String']['output'];
w: Scalars['PositiveInt']['output'];
x: Scalars['PositiveInt']['output'];
y: Scalars['PositiveInt']['output'];
};
export enum EventType {
Click = 'click',
Cms = 'cms',
Form = 'form',
Keyboard = 'keyboard',
View = 'view'
}
export enum Feature {
EmbeddableDashboard = 'EmbeddableDashboard',
HistoricalData = 'HistoricalData',
LiveData = 'LiveData',
Membership = 'Membership',
OneMillionVisitors = 'OneMillionVisitors',
OneThousandVisitors = 'OneThousandVisitors',
TenThousandVisitors = 'TenThousandVisitors',
UnlimitedMetrics = 'UnlimitedMetrics',
UnlimitedSites = 'UnlimitedSites'
}
export type Features = {
__typename?: 'Features';
memberFilter: Scalars['Boolean']['output'];
};
export type Me = {
__typename?: 'Me';
organisations: Array<MeOrganisation>;
user: User;
};
export type MeOrganisation = {
__typename?: 'MeOrganisation';
id: Scalars['String']['output'];
};
export type Metric = {
__typename?: 'Metric';
breakdownBy?: Maybe<MetricChartBreakdownBy>;
chartData?: Maybe<MetricChart>;
cssSelector?: Maybe<Scalars['String']['output']>;
dashboardId: Scalars['String']['output'];
id: Scalars['ID']['output'];
metricType: MetricType;
name: Scalars['String']['output'];
path?: Maybe<Scalars['String']['output']>;
uniqueVisitor?: Maybe<Scalars['Boolean']['output']>;
webflowCollectionId?: Maybe<Scalars['String']['output']>;
};
export type MetricChartDataArgs = {
input: MetricChartInput;
};
export type MetricByValue = {
__typename?: 'MetricByValue';
name: Scalars['String']['output'];
value: Scalars['PositiveInt']['output'];
};
export type MetricChart = {
__typename?: 'MetricChart';
data: Array<MetricChartData>;
delta?: Maybe<Scalars['Int']['output']>;
fromCache?: Maybe<Scalars['Boolean']['output']>;
lastSync?: Maybe<Scalars['Date']['output']>;
name: Scalars['String']['output'];
queryTime?: Maybe<Scalars['Float']['output']>;
total?: Maybe<Scalars['Int']['output']>;
};
export enum MetricChartBreakdownBy {
City = 'City',
ClientName = 'ClientName',
Country = 'Country',
CssClass = 'CssClass',
CssId = 'CssId',
DeviceBrand = 'DeviceBrand',
DeviceModel = 'DeviceModel',
DeviceType = 'DeviceType',
None = 'None',
OsName = 'OsName',
Path = 'Path',
Referer = 'Referer'
}
export type MetricChartData = {
__typename?: 'MetricChartData';
name: Scalars['String']['output'];
values: Array<MetricChartDataValues>;
};
export type MetricChartDataValues = {
__typename?: 'MetricChartDataValues';
label: Scalars['String']['output'];
value: Scalars['Int']['output'];
};
export type MetricChartInput = {
breakdownBy?: InputMaybe<MetricChartBreakdownBy>;
country?: InputMaybe<Scalars['String']['input']>;
cssSelector?: InputMaybe<Scalars['String']['input']>;
domain?: InputMaybe<Scalars['String']['input']>;
emailAddress?: InputMaybe<Scalars['String']['input']>;
endAt?: InputMaybe<Scalars['String']['input']>;
ipAddress?: InputMaybe<Scalars['String']['input']>;
isMember?: InputMaybe<Scalars['Boolean']['input']>;
memberId?: InputMaybe<Scalars['String']['input']>;
metricId?: InputMaybe<Scalars['String']['input']>;
metricType?: InputMaybe<MetricType>;
path?: InputMaybe<Scalars['String']['input']>;
referer?: InputMaybe<Scalars['String']['input']>;
siteId?: InputMaybe<Scalars['String']['input']>;
skipCache?: InputMaybe<Scalars['Boolean']['input']>;
startAt?: InputMaybe<Scalars['String']['input']>;
timeframe?: InputMaybe<MetricTimeframe>;
uniqueVisitor?: InputMaybe<Scalars['Boolean']['input']>;
value?: InputMaybe<Scalars['String']['input']>;
visitorId?: InputMaybe<Scalars['String']['input']>;
webflowCollectionId?: InputMaybe<Scalars['String']['input']>;
};
export type MetricEvent = {
__typename?: 'MetricEvent';
cmsCollectionItemSlug?: Maybe<Scalars['String']['output']>;
cmsCollectionSlug?: Maybe<Scalars['String']['output']>;
cmsWebflowCollectionId?: Maybe<Scalars['String']['output']>;
cmsWebflowItemId?: Maybe<Scalars['String']['output']>;
createdAt?: Maybe<Scalars['Date']['output']>;
cssClass?: Maybe<Scalars['String']['output']>;
cssId?: Maybe<Scalars['String']['output']>;
currentUrl?: Maybe<Scalars['String']['output']>;
deviceBrand?: Maybe<Scalars['String']['output']>;
deviceClientEngine?: Maybe<Scalars['String']['output']>;
deviceClientEngineVersion?: Maybe<Scalars['String']['output']>;
deviceClientName?: Maybe<Scalars['String']['output']>;
deviceClientType?: Maybe<Scalars['String']['output']>;
deviceClientVersion?: Maybe<Scalars['String']['output']>;
deviceId?: Maybe<Scalars['String']['output']>;
deviceModel?: Maybe<Scalars['String']['output']>;
deviceOsName?: Maybe<Scalars['String']['output']>;
deviceOsPlatform?: Maybe<Scalars['String']['output']>;
deviceOsVersion?: Maybe<Scalars['String']['output']>;
deviceType?: Maybe<Scalars['String']['output']>;
deviceUserAgent?: Maybe<Scalars['String']['output']>;
domain?: Maybe<Scalars['String']['output']>;
eventType: EventType;
formDisplayedAt?: Maybe<Scalars['Date']['output']>;
formLastInteractionAt?: Maybe<Scalars['Date']['output']>;
formSubmittedAt?: Maybe<Scalars['Date']['output']>;
geoIpCity?: Maybe<Scalars['String']['output']>;
geoIpCountry?: Maybe<Scalars['String']['output']>;
geoIpEu?: Maybe<Scalars['Boolean']['output']>;
geoIpLatitude?: Maybe<Scalars['Float']['output']>;
geoIpLongitude?: Maybe<Scalars['Float']['output']>;
geoIpRegion?: Maybe<Scalars['String']['output']>;
geoIpTimezone?: Maybe<Scalars['String']['output']>;
id: Scalars['ID']['output'];
ipAddress?: Maybe<Scalars['String']['output']>;
path?: Maybe<Scalars['String']['output']>;
referer?: Maybe<Scalars['String']['output']>;
siteId?: Maybe<Scalars['String']['output']>;
updatedAt?: Maybe<Scalars['Date']['output']>;
value?: Maybe<Scalars['String']['output']>;
visitorId?: Maybe<Scalars['String']['output']>;
};
export type MetricFilters = {
isMember?: InputMaybe<Scalars['Boolean']['input']>;
};
export enum MetricInterval {
Day = 'day',
Hour = 'hour',
Month = 'month'
}
export enum MetricTimeframe {
Custom = 'Custom',
Last3Months = 'Last3Months',
Last6Months = 'Last6Months',
Last7days = 'Last7days',
Last30days = 'Last30days',
LastYear = 'LastYear',
Today = 'Today',
Yesterday = 'Yesterday'
}
export enum MetricType {
Click = 'Click',
Cms = 'Cms',
CustomField = 'CustomField',
Form = 'Form',
Keyboard = 'Keyboard',
TopCountries = 'TopCountries',
TopPages = 'TopPages',
TopReferers = 'TopReferers',
View = 'View',
Visitors = 'Visitors'
}
export type MetricValue = {
__typename?: 'MetricValue';
date: Scalars['Date']['output'];
value: Scalars['PositiveInt']['output'];
};
export type MetricValueResults = {
__typename?: 'MetricValueResults';
label?: Maybe<Scalars['String']['output']>;
values: Array<MetricValue>;
};
export type MetricValuesInput = {
cssSelector?: InputMaybe<Scalars['String']['input']>;
endAt?: InputMaybe<Scalars['String']['input']>;
metricType: MetricType;
path?: InputMaybe<Scalars['String']['input']>;
siteId: Scalars['String']['input'];
startAt: Scalars['String']['input'];
webflowCollectionId?: InputMaybe<Scalars['String']['input']>;
};
export type Mutation = {
__typename?: 'Mutation';
checkScriptHeader: CheckScriptHeaderResults;
createDashboard: Dashboard;
createMetric: Metric;
createToken: Scalars['String']['output'];
deleteDashboard: Scalars['Boolean']['output'];
deleteMetric: Scalars['String']['output'];
deleteSite: Scalars['String']['output'];
findOrUpdateVisitor: Visitor;
renewToken: Scalars['String']['output'];
reportError: Scalars['Boolean']['output'];
resetCacheBySiteId: Scalars['Boolean']['output'];
sendMagicLink: Scalars['Boolean']['output'];
signIn: Scalars['String']['output'];
signUpWithWebflow: Scalars['String']['output'];
syncSiteWithWebflow: Scalars['Boolean']['output'];
syncWebflowSites: Scalars['Boolean']['output'];
updateDashboard: Dashboard;
updateMetric: Metric;
updateSiteById: Site;
updateSiteGridLayout?: Maybe<Site>;
};
export type MutationCheckScriptHeaderArgs = {
siteId: Scalars['ID']['input'];
};
export type MutationCreateDashboardArgs = {
input: CreateDashboardInput;
};
export type MutationCreateMetricArgs = {
metric: CreateMetricInput;
};
export type MutationCreateTokenArgs = {
userId: Scalars['String']['input'];
};
export type MutationDeleteDashboardArgs = {
id: Scalars['String']['input'];
};
export type MutationDeleteMetricArgs = {
metricId: Scalars['ID']['input'];
};
export type MutationDeleteSiteArgs = {
siteId: Scalars['ID']['input'];
};
export type MutationFindOrUpdateVisitorArgs = {
emailAddress?: InputMaybe<Scalars['String']['input']>;
memberstackId?: InputMaybe<Scalars['String']['input']>;
outsetaId?: InputMaybe<Scalars['String']['input']>;
visitorId: Scalars['UUID']['input'];
};
export type MutationReportErrorArgs = {
input: ReportErrorInput;
};
export type MutationResetCacheBySiteIdArgs = {
siteId: Scalars['ID']['input'];
};
export type MutationSendMagicLinkArgs = {
email: Scalars['String']['input'];
};
export type MutationSignInArgs = {
token: Scalars['String']['input'];
};
export type MutationSignUpWithWebflowArgs = {
code: Scalars['String']['input'];
};
export type MutationSyncSiteWithWebflowArgs = {
siteId: Scalars['ID']['input'];
};
export type MutationUpdateDashboardArgs = {
input: UpdateDashboardInput;
};
export type MutationUpdateMetricArgs = {
metric: UpdateMetricInput;
};
export type MutationUpdateSiteByIdArgs = {
input: UpdateSiteByIdInput;
};
export type MutationUpdateSiteGridLayoutArgs = {
newGridLayout: Array<CreateSiteGridLayoutItemInput>;
siteId: Scalars['ID']['input'];
};
export type Organisation = {
__typename?: 'Organisation';
features: Array<Feature>;
id: Scalars['ID']['output'];
};
export type PageInfo = {
__typename?: 'PageInfo';
hasNextPage: Scalars['Boolean']['output'];
};
export type Pagination = {
__typename?: 'Pagination';
hasMore: Scalars['Boolean']['output'];
total: Scalars['PositiveInt']['output'];
};
export type PaginationInput = {
cursor?: InputMaybe<Scalars['String']['input']>;
limit?: InputMaybe<Scalars['PositiveInt']['input']>;
};
export type Query = {
__typename?: 'Query';
adminReport: AdminReport;
chartData: MetricChart;
dashboard: Dashboard;
dashboardBySiteId: Array<Dashboard>;
eventTypes: Array<EventType>;
features?: Maybe<Features>;
maintenance: Scalars['Boolean']['output'];
me?: Maybe<Me>;
metric: Metric;
metricByValues: Array<MetricByValue>;
metricTypes: Array<MetricType>;
metricValues: Array<MetricValueResults>;
metricsByDashboardId: Array<Metric>;
metricsBySiteId: Array<Metric>;
organisation: Organisation;
queueEvents?: Maybe<Array<Maybe<QueueEvent>>>;
site: Site;
/** @deprecated Use 'site' instead. */
siteById: Site;
siteByWebflowId: Site;
siteHtmlElements: Array<SiteHtmlElement>;
sites: SitePaginated;
stripeCustomerPortalLink: Scalars['String']['output'];
users: UserPaginated;
visitor?: Maybe<Visitor>;
visitors: VisitorPaginated;
};
export type QueryChartDataArgs = {
input: MetricChartInput;
};
export type QueryDashboardArgs = {
id: Scalars['String']['input'];
};
export type QueryDashboardBySiteIdArgs = {
siteId: Scalars['String']['input'];
};
export type QueryMetricArgs = {
id: Scalars['ID']['input'];
};
export type QueryMetricByValuesArgs = {
filters?: InputMaybe<MetricFilters>;
input: MetricValuesInput;
};
export type QueryMetricValuesArgs = {
filters?: InputMaybe<MetricFilters>;
input: MetricValuesInput;
};
export type QueryMetricsByDashboardIdArgs = {
dashboardById: Scalars['ID']['input'];
};
export type QueryMetricsBySiteIdArgs = {
siteId: Scalars['ID']['input'];
};
export type QueryOrganisationArgs = {
organisationId?: InputMaybe<Scalars['ID']['input']>;
siteId?: InputMaybe<Scalars['String']['input']>;
};
export type QueryQueueEventsArgs = {
siteId: Scalars['String']['input'];
};
export type QuerySiteArgs = {
id: Scalars['ID']['input'];
};
export type QuerySiteByIdArgs = {
id: Scalars['ID']['input'];
};
export type QuerySiteByWebflowIdArgs = {
webflowId: Scalars['String']['input'];
};
export type QuerySiteHtmlElementsArgs = {
siteId: Scalars['ID']['input'];
};
export type QuerySitesArgs = {
cursor?: InputMaybe<Scalars['String']['input']>;
filters?: InputMaybe<SitesFilters>;
first?: InputMaybe<Scalars['PositiveInt']['input']>;
organisationId: Scalars['String']['input'];
};
export type QueryUsersArgs = {
cursor?: InputMaybe<Scalars['String']['input']>;
first?: InputMaybe<Scalars['PositiveInt']['input']>;
search?: InputMaybe<Scalars['String']['input']>;
};
export type QueryVisitorArgs = {
id: Scalars['ID']['input'];
};
export type QueryVisitorsArgs = {
filters: VisitorsFilters;
first?: InputMaybe<Scalars['PositiveInt']['input']>;
offset?: InputMaybe<Scalars['PositiveInt']['input']>;
};
export type QueueEvent = {
__typename?: 'QueueEvent';
cmsItemIndex?: Maybe<Scalars['Int']['output']>;
cmsItemSlug?: Maybe<Scalars['String']['output']>;
cmsListIndex?: Maybe<Scalars['Int']['output']>;
createdAt?: Maybe<Scalars['String']['output']>;
cssClass?: Maybe<Scalars['String']['output']>;
cssId?: Maybe<Scalars['String']['output']>;
domain?: Maybe<Scalars['String']['output']>;
eventType?: Maybe<Scalars['String']['output']>;
formStage?: Maybe<Scalars['String']['output']>;
id?: Maybe<Scalars['String']['output']>;
ip?: Maybe<Scalars['String']['output']>;
path?: Maybe<Scalars['String']['output']>;
referer?: Maybe<Scalars['String']['output']>;
siteId?: Maybe<Scalars['String']['output']>;
userAgent?: Maybe<Scalars['String']['output']>;
value?: Maybe<Scalars['String']['output']>;
visitorId?: Maybe<Scalars['String']['output']>;
};
export type ReportErrorInput = {
error?: InputMaybe<Scalars['JSON']['input']>;
from: Scalars['String']['input'];
message?: InputMaybe<Scalars['String']['input']>;
};
export type Site = {
__typename?: 'Site';
collections: Array<WebflowCollection>;
createdAt: Scalars['Date']['output'];
dashboards?: Maybe<Array<Dashboard>>;
domains?: Maybe<Array<Scalars['String']['output']>>;
eventSources?: Maybe<Array<Scalars['String']['output']>>;
gridLayout?: Maybe<Array<SiteGridLayoutItem>>;
id: Scalars['ID']['output'];
isEnabled: Scalars['Boolean']['output'];
isPublic: Scalars['Boolean']['output'];
metrics?: Maybe<Array<Metric>>;
name: Scalars['String']['output'];
organisationId: Scalars['String']['output'];
previewUrl?: Maybe<Scalars['String']['output']>;
shortName?: Maybe<Scalars['String']['output']>;
totalMetricsCount?: Maybe<Scalars['PositiveInt']['output']>;
updatedAt: Scalars['Date']['output'];
webflowId: Scalars['String']['output'];
};
export type SiteEventSourcesArgs = {
filter?: InputMaybe<Scalars['String']['input']>;
};
export type SiteEdge = {
__typename?: 'SiteEdge';
cursor: Scalars['String']['output'];
node: Site;
};
export type SiteGridLayoutItem = {
__typename?: 'SiteGridLayoutItem';
h: Scalars['PositiveInt']['output'];
id: Scalars['String']['output'];
w: Scalars['PositiveInt']['output'];
x: Scalars['PositiveInt']['output'];
y: Scalars['PositiveInt']['output'];
};
export type SiteHtmlElement = {
__typename?: 'SiteHtmlElement';
html: Scalars['String']['output'];
id: Scalars['String']['output'];
text?: Maybe<Scalars['String']['output']>;
};
export type SitePaginated = {
__typename?: 'SitePaginated';
edges: Array<SiteEdge>;
pageInfo: PageInfo;
totalCount: Scalars['PositiveInt']['output'];
};
export type SitesFilters = {
name?: InputMaybe<Scalars['String']['input']>;
organisationId?: InputMaybe<Scalars['String']['input']>;
userId?: InputMaybe<Scalars['String']['input']>;
};
export type UpdateDashboardGridLayoutItem = {
h: Scalars['PositiveInt']['input'];
id: Scalars['String']['input'];
w: Scalars['PositiveInt']['input'];
x: Scalars['PositiveInt']['input'];
y: Scalars['PositiveInt']['input'];
};
export type UpdateDashboardInput = {
gridLayout?: InputMaybe<Array<UpdateDashboardGridLayoutItem>>;
id: Scalars['String']['input'];
isPublic?: InputMaybe<Scalars['Boolean']['input']>;
name?: InputMaybe<Scalars['String']['input']>;
};
export type UpdateMetricInput = {
breakdownBy?: InputMaybe<MetricChartBreakdownBy>;
cssSelector?: InputMaybe<Scalars['String']['input']>;
id: Scalars['ID']['input'];
metricType?: InputMaybe<MetricType>;
name?: InputMaybe<Scalars['String']['input']>;
path?: InputMaybe<Scalars['String']['input']>;
uniqueVisitor?: InputMaybe<Scalars['Boolean']['input']>;
webflowCollectionId?: InputMaybe<Scalars['String']['input']>;
};
export type UpdateSiteByIdInput = {
id: Scalars['String']['input'];
isPublic?: InputMaybe<Scalars['Boolean']['input']>;
};
export type User = {
__typename?: 'User';
createdAt: Scalars['Date']['output'];
email: Scalars['String']['output'];
firstname?: Maybe<Scalars['String']['output']>;
id: Scalars['String']['output'];
lastname?: Maybe<Scalars['String']['output']>;
};
export type UserEdge = {
__typename?: 'UserEdge';
cursor: Scalars['String']['output'];
node: User;
};
export type UserPaginated = {
__typename?: 'UserPaginated';
edges: Array<UserEdge>;
pageInfo: PageInfo;
totalCount: Scalars['PositiveInt']['output'];
};
export type Visitor = {
__typename?: 'Visitor';
city?: Maybe<Scalars['String']['output']>;
country?: Maybe<Scalars['String']['output']>;
device?: Maybe<Scalars['String']['output']>;
emailAddress?: Maybe<Scalars['String']['output']>;
events: Array<MetricEvent>;
id: Scalars['ID']['output'];
initialReferer?: Maybe<Scalars['String']['output']>;
lastInteraction: Scalars['Date']['output'];
};
export type VisitorEdge = {
__typename?: 'VisitorEdge';
cursor: Scalars['String']['output'];
node: Visitor;
};
export type VisitorInput = {
emailAddress?: InputMaybe<Scalars['String']['input']>;
id: Scalars['UUID']['input'];
memberstackId?: InputMaybe<Scalars['String']['input']>;
outsetaId?: InputMaybe<Scalars['String']['input']>;
};
export type VisitorPaginated = {
__typename?: 'VisitorPaginated';
edges: Array<VisitorEdge>;
pageInfo: PageInfo;
totalCount: Scalars['PositiveInt']['output'];
};
export type VisitorsFilters = {
emailAddress?: InputMaybe<Scalars['String']['input']>;
endAt?: InputMaybe<Scalars['Date']['input']>;
isMember?: InputMaybe<Scalars['Boolean']['input']>;
siteId: Scalars['String']['input'];
startAt: Scalars['Date']['input'];
visitorId?: InputMaybe<Scalars['String']['input']>;
};
export type WebflowCollection = {
__typename?: 'WebflowCollection';
fields: Array<WebflowCollectionFields>;
id: Scalars['String']['output'];
items: Array<WebflowCollectionItems>;
name: Scalars['String']['output'];
};
export type WebflowCollectionFields = {
__typename?: 'WebflowCollectionFields';
id: Scalars['String']['output'];
name: Scalars['String']['output'];
slug: Scalars['String']['output'];
type: Scalars['String']['output'];
};
export type WebflowCollectionItems = {
__typename?: 'WebflowCollectionItems';
id: Scalars['String']['output'];
name: Scalars['String']['output'];
};
export type WebflowSites = {
__typename?: 'WebflowSites';
name: Scalars['String']['output'];
};
export type ChartDataQueryVariables = Exact<{
input: MetricChartInput;
}>;
export type ChartDataQuery = { __typename?: 'Query', chartData: { __typename?: 'MetricChart', name: string, delta?: number | null | undefined, data: Array<{ __typename?: 'MetricChartData', name: string, values: Array<{ __typename?: 'MetricChartDataValues', label: string, value: number }> }> } };
export type SiteByWebflowIdQueryVariables = Exact<{
webflowId: Scalars['String']['input'];
}>;
export type SiteByWebflowIdQuery = { __typename?: 'Query', siteByWebflowId: { __typename?: 'Site', id: string } };
export const ChartDataDocument = gql`
query chartData($input: MetricChartInput!) {
chartData(input: $input) {
name
data {
name
values {
label
value
}
}
delta
}
}
`;
/**
* __useChartDataQuery__
*
* To run a query within a React component, call `useChartDataQuery` and pass it any options that fit your needs.
* When your component renders, `useChartDataQuery` returns an object from Apollo Client that contains loading, error, and data properties
* you can use to render your UI.
*
* @param baseOptions options that will be passed into the query, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options;
*
* @example
* const { data, loading, error } = useChartDataQuery({
* variables: {
* input: // value for 'input'
* },
* });
*/
export function useChartDataQuery(baseOptions: Apollo.QueryHookOptions<ChartDataQuery, ChartDataQueryVariables>) {
const options = {...defaultOptions, ...baseOptions}
return Apollo.useQuery<ChartDataQuery, ChartDataQueryVariables>(ChartDataDocument, options);
}
export function useChartDataLazyQuery(baseOptions?: Apollo.LazyQueryHookOptions<ChartDataQuery, ChartDataQueryVariables>) {
const options = {...defaultOptions, ...baseOptions}
return Apollo.useLazyQuery<ChartDataQuery, ChartDataQueryVariables>(ChartDataDocument, options);
}
export type ChartDataQueryHookResult = ReturnType<typeof useChartDataQuery>;
export type ChartDataLazyQueryHookResult = ReturnType<typeof useChartDataLazyQuery>;
export type ChartDataQueryResult = Apollo.QueryResult<ChartDataQuery, ChartDataQueryVariables>;
export const SiteByWebflowIdDocument = gql`
query siteByWebflowId($webflowId: String!) {
siteByWebflowId(webflowId: $webflowId) {
id
}
}
`;
/**
* __useSiteByWebflowIdQuery__
*
* To run a query within a React component, call `useSiteByWebflowIdQuery` and pass it any options that fit your needs.
* When your component renders, `useSiteByWebflowIdQuery` returns an object from Apollo Client that contains loading, error, and data properties
* you can use to render your UI.
*
* @param baseOptions options that will be passed into the query, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options;
*
* @example
* const { data, loading, error } = useSiteByWebflowIdQuery({
* variables: {
* webflowId: // value for 'webflowId'
* },
* });
*/
export function useSiteByWebflowIdQuery(baseOptions: Apollo.QueryHookOptions<SiteByWebflowIdQuery, SiteByWebflowIdQueryVariables>) {
const options = {...defaultOptions, ...baseOptions}
return Apollo.useQuery<SiteByWebflowIdQuery, SiteByWebflowIdQueryVariables>(SiteByWebflowIdDocument, options);
}
export function useSiteByWebflowIdLazyQuery(baseOptions?: Apollo.LazyQueryHookOptions<SiteByWebflowIdQuery, SiteByWebflowIdQueryVariables>) {
const options = {...defaultOptions, ...baseOptions}
return Apollo.useLazyQuery<SiteByWebflowIdQuery, SiteByWebflowIdQueryVariables>(SiteByWebflowIdDocument, options);
}
export type SiteByWebflowIdQueryHookResult = ReturnType<typeof useSiteByWebflowIdQuery>;
export type SiteByWebflowIdLazyQueryHookResult = ReturnType<typeof useSiteByWebflowIdLazyQuery>;
export type SiteByWebflowIdQueryResult = Apollo.QueryResult<SiteByWebflowIdQuery, SiteByWebflowIdQueryVariables>;

View File

@ -0,0 +1,13 @@
query chartData($input: MetricChartInput!) {
chartData(input: $input) {
name
data {
name
values {
label
value
}
}
delta
}
}

View File

@ -0,0 +1,5 @@
query siteByWebflowId($webflowId: String!) {
siteByWebflowId(webflowId: $webflowId) {
id
}
}

2
src/hooks/index.ts Normal file
View File

@ -0,0 +1,2 @@
export * from "./useCurrentSiteInfo";
export * from "./useWebflowInfo";

View File

@ -0,0 +1,32 @@
import { useSiteByWebflowIdQuery } from "../generated/graphql";
import { useWebflowInfo } from "./useWebflowInfo";
interface UseCurrentSiteIdResults {
loading: boolean;
error?: string;
data: {
webflowId?: string;
nocodelyticsId?: string;
};
}
export const useCurrentSiteInfo = (): UseCurrentSiteIdResults => {
const { webflowSiteLoading, webflowId, webflowError } = useWebflowInfo();
const siteByWebflowIdQueryResults = useSiteByWebflowIdQuery({
variables: {
webflowId,
},
skip: !webflowId,
});
const loading = webflowSiteLoading || siteByWebflowIdQueryResults.loading;
const error = webflowError || siteByWebflowIdQueryResults.error?.message;
return {
loading,
error,
data: {
webflowId,
nocodelyticsId: siteByWebflowIdQueryResults.data?.siteByWebflowId.id,
},
};
};

View File

@ -0,0 +1,23 @@
import { useState, useEffect } from "react";
export const useWebflowInfo = () => {
const [webflowSiteLoading, setWebflowSiteLoading] = useState<boolean>(true);
const [webflowId, setWebflowId] = useState<string>("");
const [webflowError, setWebflowError] = useState<string>("");
useEffect(() => {
webflow
.getSiteInfo()
.then(({ siteId }) => {
setWebflowId(siteId);
setWebflowSiteLoading(false);
})
.catch((error) => setWebflowError(error));
}, []);
return {
webflowSiteLoading,
webflowId,
webflowError,
};
};

10
src/main.tsx Normal file
View File

@ -0,0 +1,10 @@
import React from "react";
import { createRoot } from "react-dom/client";
import { App } from "./App";
if (typeof webflow === "undefined") {
alert("Error: `webflow` object is not defined.");
}
const container = document.getElementById("root");
const root = createRoot(container!);
root.render(<App />);

24
src/pages/DebugPage.tsx Normal file
View File

@ -0,0 +1,24 @@
import React from "react";
import { config } from "../config";
export const DebugPage: React.FC = () => {
return (
<ul>
<li>
Token:{" "}
{
<input
type="text"
defaultValue={localStorage.getItem(config.localstorageKeys.token)!}
onChange={(event) =>
localStorage.setItem(
config.localstorageKeys.token,
event.target.value
)
}
/>
}
</li>
</ul>
);
};

View File

@ -0,0 +1,50 @@
import React, { useEffect, useState } from "react";
import { Dropdown } from "../components";
import { Chart } from "../containers/Chart";
import { MetricType } from "../generated/graphql";
export const MetricDetailsPage: React.FC = () => {
const [path, setPath] = useState<string>();
const [timeframe, setTimeframe] = useState<string>("Last30days");
useEffect(() => {
const unsubscribeCurrentPage = webflow.subscribe("currentpage", (page) => {
setPath(`/${page.getSlug()}`);
});
webflow.getCurrentPage().then((page) => setPath(`/${page.getSlug()}`));
return () => {
unsubscribeCurrentPage();
};
}, []);
return (
<div>
<Dropdown
selected="Last30days"
options={[
{ label: "Last 7 days", value: "Last7days" },
{ label: "Last 30 days", value: "Last30days" },
{ label: "Last 3 Months", value: "Last3Months" },
{ label: "Last 6 Months", value: "Last6Months" },
{ label: "Last Year", value: "LastYear" },
]}
onChange={(value) => {
setTimeframe(value);
}}
/>
<div style={{ marginTop: 10 }}>
<Chart input={{ path, metricType: MetricType.View, timeframe }} />
</div>
<div style={{ marginTop: 30 }}>
<Chart
input={{
path,
metricType: MetricType.Visitors,
uniqueVisitor: true,
timeframe,
}}
/>
</div>
</div>
);
};

2
src/pages/index.tsx Normal file
View File

@ -0,0 +1,2 @@
export * from "./MetricDetailsPage";
export * from "./DebugPage";

33
src/theme.ts Normal file
View File

@ -0,0 +1,33 @@
import { CSSProperties } from "react";
type Variant = "primary" | "secondary";
type ThemeVariants = {
[K in Variant]?: CSSProperties;
};
type Element = "text" | "background" | "border";
type Theme = {
[K in Element]: ThemeVariants;
};
/**
* Quick hack with minimal info until webflow publishes a components lib
*/
export const theme: Theme = {
text: {
primary: { color: "#D9D9D9" },
},
background: {
secondary: { backgroundColor: "#5E5E5E" },
},
border: {
primary: {
borderColor: "#363636",
borderWidth: 1,
borderRadius: 1,
borderStyle: "solid",
},
},
};

26
tsconfig.json Normal file
View File

@ -0,0 +1,26 @@
{
"compilerOptions": {
"target": "esnext",
"lib": ["DOM", "DOM.Iterable", "esnext"],
"types": ["vite/client", "node", "css-font-loading-module"],
"allowJs": false,
"skipLibCheck": true,
"esModuleInterop": true,
"allowSyntheticDefaultImports": true,
"strict": true,
"forceConsistentCasingInFileNames": true,
"module": "esnext",
"moduleResolution": "node",
"resolveJsonModule": true,
"isolatedModules": true,
"jsx": "react",
"rootDir": "./",
"outDir": ".build"
},
"include": [
"./src",
"vitest.config.ts",
"vitest.config.ts",
"node_modules/@webflow/designer-extension-typings"
]
}

34
vite.config.ts Normal file
View File

@ -0,0 +1,34 @@
import reactRefresh from "@vitejs/plugin-react-refresh";
import dotenv from "dotenv";
import { defineConfig } from "vite";
dotenv.config();
// eslint-disable-next-line import/no-default-export
export default defineConfig({
base: "./",
plugins: [reactRefresh()],
server: {
host: true,
port: 1337,
},
optimizeDeps: {
exclude: ["./webflow"],
},
define: {
"process.env": {
...process.env,
},
},
resolve: {
alias: {
"~bootstrap": "bootstrap",
},
},
build: {
outDir: "public",
sourcemap: true,
rollupOptions: {},
},
});

5
webflow.json Normal file
View File

@ -0,0 +1,5 @@
{
"name": "nocodelytics",
"publicDir": "public",
"size": "comfortable"
}