Skip to content

Commit 32ba14b

Browse files
authored
Merge branch 'master' into release-v212
2 parents cf52c05 + 5766478 commit 32ba14b

File tree

72 files changed

+1719
-504
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

72 files changed

+1719
-504
lines changed

.github/workflows/build-private-images-ghcr.yml

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,4 +77,12 @@ jobs:
7777
method: 'POST'
7878
customHeaders: '{"Content-Type": "application/json"}'
7979
escapeData: 'true'
80-
data: '{"content": "<h1>🚀 New changes are about to be deployed to production!</h1><br/><h3>👷 Author: ${{ github.actor }}</h3><br/><p>📝 Commit message: ${{ github.event.head_commit.message }}</p><br/>"}'
80+
data: '{"content": "<h1>🚀 New changes are about to be deployed to production!</h1><br/><h3>👷 Author: ${{ github.actor }}</h3><br/><p>📝 Commit message: ${{ github.event.head_commit.message }}</p><br/>"}'
81+
82+
- name: Set Honeycomb marker on success
83+
if: ${{ success() && github.ref == 'refs/heads/master' }}
84+
uses: cnkk/honeymarker-action@1bd92aec746e38efe43a0faee94ced1ebb930712
85+
with:
86+
apikey: ${{ secrets.HONEYCOMB_MARKER_APIKEY }}
87+
dataset: 'plausible-prod'
88+
message: "${{ github.sha }}"

.github/workflows/node.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ jobs:
2626
node-version: ${{steps.versions.outputs.nodejs}}
2727
- run: npm install --prefix ./assets
2828
- run: npm install --prefix ./tracker
29+
- run: npm run generate-types --prefix ./assets && git diff --exit-code -- ./assets/js/types
2930
- run: npm run typecheck --prefix ./assets
3031
- run: npm run lint --prefix ./assets
3132
- run: npm run check-format --prefix ./assets

Dockerfile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ RUN apk upgrade --no-cache
6969
RUN apk add --no-cache openssl ncurses libstdc++ libgcc ca-certificates \
7070
&& if [ "$MIX_ENV" = "ce" ]; then apk add --no-cache certbot; fi
7171

72-
COPY --from=buildcontainer --chmod=a+rX /app/_build/${MIX_ENV}/rel/plausible /app
72+
COPY --from=buildcontainer --chmod=555 /app/_build/${MIX_ENV}/rel/plausible /app
7373
COPY --chmod=755 ./rel/docker-entrypoint.sh /entrypoint.sh
7474

7575
# we need to allow "others" access to app folder, because

assets/js/dashboard/components/search-input.tsx

Lines changed: 30 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
/** @format */
22

3-
import React, { ChangeEventHandler, useCallback, useRef } from 'react'
3+
import React, { ChangeEventHandler, useCallback, useState, useRef } from 'react'
44
import { isModifierPressed, Keybind } from '../keybinding'
55
import { useDebounce } from '../custom-hooks'
66
import classNames from 'classnames'
@@ -13,6 +13,7 @@ export const SearchInput = ({
1313
onSearch: (value: string) => void
1414
}) => {
1515
const searchBoxRef = useRef<HTMLInputElement>(null)
16+
const [isFocused, setIsFocused] = useState(false)
1617

1718
const onSearchInputChange: ChangeEventHandler<HTMLInputElement> = useCallback(
1819
(event) => {
@@ -22,13 +23,25 @@ export const SearchInput = ({
2223
)
2324
const debouncedOnSearchInputChange = useDebounce(onSearchInputChange)
2425

25-
const blurSearchBox = useCallback((event: KeyboardEvent) => {
26-
const searchBox = searchBoxRef.current
27-
if (searchBox?.contains(event.target as HTMLElement)) {
28-
searchBox.blur()
29-
event.stopPropagation()
30-
}
31-
}, [])
26+
const blurSearchBox = useCallback(
27+
(event: KeyboardEvent) => {
28+
if (isFocused) {
29+
searchBoxRef.current?.blur()
30+
event.stopPropagation()
31+
}
32+
},
33+
[isFocused]
34+
)
35+
36+
const focusSearchBox = useCallback(
37+
(event: KeyboardEvent) => {
38+
if (!isFocused) {
39+
searchBoxRef.current?.focus()
40+
event.stopPropagation()
41+
}
42+
},
43+
[isFocused]
44+
)
3245

3346
return (
3447
<>
@@ -38,10 +51,18 @@ export const SearchInput = ({
3851
handler={blurSearchBox}
3952
shouldIgnoreWhen={[isModifierPressed]}
4053
/>
54+
<Keybind
55+
keyboardKey="/"
56+
type="keyup"
57+
handler={focusSearchBox}
58+
shouldIgnoreWhen={[isModifierPressed]}
59+
/>
4160
<input
61+
onBlur={() => setIsFocused(false)}
62+
onFocus={() => setIsFocused(true)}
4263
ref={searchBoxRef}
4364
type="text"
44-
placeholder="Search"
65+
placeholder={isFocused ? 'Search' : 'Press / to search'}
4566
className={classNames(
4667
'shadow-sm dark:bg-gray-900 dark:text-gray-100 focus:ring-indigo-500 focus:border-indigo-500 block sm:text-sm border-gray-300 dark:border-gray-500 rounded-md dark:bg-gray-800 w-48',
4768
className

assets/js/dashboard/router.tsx

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,11 @@ export const sourcesRoute = {
6060
element: <SourcesModal currentView="sources" />
6161
}
6262

63+
export const channelsRoute = {
64+
path: 'channels',
65+
element: <SourcesModal currentView="channels" />
66+
}
67+
6368
export const utmMediumsRoute = {
6469
path: 'utm_mediums',
6570
element: <SourcesModal currentView="utm_mediums" />
@@ -195,6 +200,7 @@ export function createAppRouter(site: PlausibleSite) {
195200
children: [
196201
{ index: true, element: <DashboardKeybinds /> },
197202
sourcesRoute,
203+
channelsRoute,
198204
utmMediumsRoute,
199205
utmSourcesRoute,
200206
utmCampaignsRoute,

assets/js/dashboard/stats/modals/breakdown-modal.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -170,7 +170,7 @@ export default function BreakdownModal<TListItem extends { name: string }>({
170170
<BreakdownTable<TListItem>
171171
title={reportInfo.title}
172172
{...apiState}
173-
onSearch={setSearch}
173+
onSearch={searchEnabled ? setSearch : undefined}
174174
columns={columns}
175175
/>
176176
)

assets/js/dashboard/stats/modals/filter-modal.js

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import { useParams } from 'react-router-dom';
44
import Modal from './modal';
55
import { EVENT_PROPS_PREFIX, FILTER_GROUP_TO_MODAL_TYPE, formatFilterGroup, FILTER_OPERATIONS, getFilterGroup, FILTER_MODAL_TO_FILTER_GROUP, cleanLabels } from '../../util/filters';
66
import { useQueryContext } from '../../query-context';
7+
import { useSiteContext } from '../../site-context';
78
import { isModifierPressed, isTyping } from '../../keybinding';
89
import FilterModalGroup from "./filter-modal-group";
910
import { rootRoute } from '../../router';
@@ -134,6 +135,14 @@ class FilterModal extends React.Component {
134135
})
135136
}
136137

138+
getFilterGroups() {
139+
const groups = FILTER_MODAL_TO_FILTER_GROUP[this.props.modalType]
140+
if (this.props.modalType === 'source' && !this.props.site.flags.channels) {
141+
return groups.filter((group) => group !== 'channel')
142+
}
143+
return groups
144+
}
145+
137146
render() {
138147
return (
139148
<Modal maxWidth="460px">
@@ -144,7 +153,7 @@ class FilterModal extends React.Component {
144153
<div className="mt-4 border-b border-gray-300"></div>
145154
<main className="modal__content">
146155
<form className="flex flex-col" onSubmit={this.handleSubmit.bind(this)}>
147-
{FILTER_MODAL_TO_FILTER_GROUP[this.props.modalType].map((filterGroup) => (
156+
{this.getFilterGroups().map((filterGroup) => (
148157
<FilterModalGroup
149158
key={filterGroup}
150159
filterGroup={filterGroup}
@@ -189,12 +198,14 @@ export default function FilterModalWithRouter(props) {
189198
const navigate = useAppNavigate();
190199
const { field } = useParams()
191200
const { query } = useQueryContext()
201+
const site = useSiteContext()
192202
return (
193203
<FilterModal
194204
{...props}
195205
modalType={field || 'page'}
196206
query={query}
197207
navigate={navigate}
208+
site={site}
198209
/>
199210
)
200211
}

assets/js/dashboard/stats/modals/sources.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,9 @@ const VIEWS = {
2222
)
2323
}
2424
},
25+
channels: {
26+
info: { title: 'Top Acquisition Channels', dimension: 'channel', endpoint: '/channels', dimensionLabel: 'Channel', defaultOrder: ["visitors", SortDirection.desc] }
27+
},
2528
utm_mediums: {
2629
info: { title: 'Top UTM Mediums', dimension: 'utm_medium', endpoint: '/utm_mediums', dimensionLabel: 'UTM Medium', defaultOrder: ["visitors", SortDirection.desc] }
2730
},

assets/js/dashboard/stats/sources/source-list.js

Lines changed: 42 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ import classNames from 'classnames';
1212
import ImportedQueryUnsupportedWarning from '../imported-query-unsupported-warning';
1313
import { useQueryContext } from '../../query-context';
1414
import { useSiteContext } from '../../site-context';
15-
import { sourcesRoute, utmCampaignsRoute, utmContentsRoute, utmMediumsRoute, utmSourcesRoute, utmTermsRoute } from '../../router';
15+
import { sourcesRoute, channelsRoute, utmCampaignsRoute, utmContentsRoute, utmMediumsRoute, utmSourcesRoute, utmTermsRoute } from '../../router';
1616

1717
const UTM_TAGS = {
1818
utm_medium: { label: 'UTM Medium', shortLabel: 'UTM Medium', endpoint: '/utm_mediums' },
@@ -67,6 +67,41 @@ function AllSources({ afterFetchData }) {
6767
)
6868
}
6969

70+
function Channels({ afterFetchData }) {
71+
const { query } = useQueryContext();
72+
const site = useSiteContext();
73+
74+
function fetchData() {
75+
return api.get(url.apiPath(site, '/channels'), query, { limit: 9 })
76+
}
77+
78+
function getFilterFor(listItem) {
79+
return {
80+
prefix: 'channel',
81+
filter: ["is", "channel", [listItem['name']]]
82+
}
83+
}
84+
85+
function chooseMetrics() {
86+
return [
87+
metrics.createVisitors({ meta: { plot: true } }),
88+
hasGoalFilter(query) && metrics.createConversionRate(),
89+
].filter(metric => !!metric)
90+
}
91+
92+
return (
93+
<ListReport
94+
fetchData={fetchData}
95+
afterFetchData={afterFetchData}
96+
getFilterFor={getFilterFor}
97+
keyLabel="Channel"
98+
metrics={chooseMetrics()}
99+
detailsLinkProps={{ path: channelsRoute.path, search: (search) => search }}
100+
color="bg-blue-50"
101+
/>
102+
)
103+
}
104+
70105
function UTMSources({ tab, afterFetchData }) {
71106
const { query } = useQueryContext();
72107
const site = useSiteContext();
@@ -79,7 +114,7 @@ function UTMSources({ tab, afterFetchData }) {
79114
utm_content: utmContentsRoute,
80115
utm_term: utmTermsRoute,
81116
}[tab]
82-
117+
83118
function fetchData() {
84119
return api.get(url.apiPath(site, utmTag.endpoint), query, { limit: 9 })
85120
}
@@ -138,6 +173,9 @@ export default function SourceList() {
138173
return (
139174
<div className="flex text-xs font-medium text-gray-500 dark:text-gray-400 space-x-2">
140175
<div className={currentTab === 'all' ? activeClass : defaultClass} onClick={setTab('all')}>All</div>
176+
{site.flags.channels &&
177+
<div className={currentTab === 'channels' ? activeClass : defaultClass} onClick={setTab('channels')}>Channels</div>
178+
}
141179

142180
<Menu as="div" className="relative inline-block text-left">
143181
<div>
@@ -187,6 +225,8 @@ export default function SourceList() {
187225
function renderContent() {
188226
if (currentTab === 'all') {
189227
return <AllSources afterFetchData={afterFetchData} />
228+
} else if (currentTab == 'channels') {
229+
return <Channels afterFetchData={afterFetchData} />
190230
} else {
191231
return <UTMSources tab={currentTab} afterFetchData={afterFetchData} />
192232
}

assets/js/dashboard/util/filters.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import { useQueryContext } from '../query-context'
66

77
export const FILTER_MODAL_TO_FILTER_GROUP = {
88
page: ['page', 'entry_page', 'exit_page'],
9-
source: ['source', 'referrer'],
9+
source: ['source', 'channel', 'referrer'],
1010
location: ['country', 'region', 'city'],
1111
screen: ['screen'],
1212
browser: ['browser', 'browser_version'],
@@ -273,6 +273,7 @@ export const formattedFilters = {
273273
prop_key: 'Property',
274274
prop_value: 'Value',
275275
source: 'Source',
276+
channel: 'Channel',
276277
utm_medium: 'UTM Medium',
277278
utm_source: 'UTM Source',
278279
utm_campaign: 'UTM Campaign',

0 commit comments

Comments
 (0)