Skip to content

Commit

Permalink
Merge branch 'master' into release-v212
Browse files Browse the repository at this point in the history
  • Loading branch information
ruslandoga authored Sep 19, 2024
2 parents cf52c05 + 5766478 commit 32ba14b
Show file tree
Hide file tree
Showing 72 changed files with 1,719 additions and 504 deletions.
10 changes: 9 additions & 1 deletion .github/workflows/build-private-images-ghcr.yml
Original file line number Diff line number Diff line change
Expand Up @@ -77,4 +77,12 @@ jobs:
method: 'POST'
customHeaders: '{"Content-Type": "application/json"}'
escapeData: 'true'
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/>"}'
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/>"}'

- name: Set Honeycomb marker on success
if: ${{ success() && github.ref == 'refs/heads/master' }}
uses: cnkk/honeymarker-action@1bd92aec746e38efe43a0faee94ced1ebb930712
with:
apikey: ${{ secrets.HONEYCOMB_MARKER_APIKEY }}
dataset: 'plausible-prod'
message: "${{ github.sha }}"
1 change: 1 addition & 0 deletions .github/workflows/node.yml
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ jobs:
node-version: ${{steps.versions.outputs.nodejs}}
- run: npm install --prefix ./assets
- run: npm install --prefix ./tracker
- run: npm run generate-types --prefix ./assets && git diff --exit-code -- ./assets/js/types
- run: npm run typecheck --prefix ./assets
- run: npm run lint --prefix ./assets
- run: npm run check-format --prefix ./assets
Expand Down
2 changes: 1 addition & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ RUN apk upgrade --no-cache
RUN apk add --no-cache openssl ncurses libstdc++ libgcc ca-certificates \
&& if [ "$MIX_ENV" = "ce" ]; then apk add --no-cache certbot; fi

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

# we need to allow "others" access to app folder, because
Expand Down
39 changes: 30 additions & 9 deletions assets/js/dashboard/components/search-input.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/** @format */

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

const onSearchInputChange: ChangeEventHandler<HTMLInputElement> = useCallback(
(event) => {
Expand All @@ -22,13 +23,25 @@ export const SearchInput = ({
)
const debouncedOnSearchInputChange = useDebounce(onSearchInputChange)

const blurSearchBox = useCallback((event: KeyboardEvent) => {
const searchBox = searchBoxRef.current
if (searchBox?.contains(event.target as HTMLElement)) {
searchBox.blur()
event.stopPropagation()
}
}, [])
const blurSearchBox = useCallback(
(event: KeyboardEvent) => {
if (isFocused) {
searchBoxRef.current?.blur()
event.stopPropagation()
}
},
[isFocused]
)

const focusSearchBox = useCallback(
(event: KeyboardEvent) => {
if (!isFocused) {
searchBoxRef.current?.focus()
event.stopPropagation()
}
},
[isFocused]
)

return (
<>
Expand All @@ -38,10 +51,18 @@ export const SearchInput = ({
handler={blurSearchBox}
shouldIgnoreWhen={[isModifierPressed]}
/>
<Keybind
keyboardKey="/"
type="keyup"
handler={focusSearchBox}
shouldIgnoreWhen={[isModifierPressed]}
/>
<input
onBlur={() => setIsFocused(false)}
onFocus={() => setIsFocused(true)}
ref={searchBoxRef}
type="text"
placeholder="Search"
placeholder={isFocused ? 'Search' : 'Press / to search'}
className={classNames(
'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',
className
Expand Down
6 changes: 6 additions & 0 deletions assets/js/dashboard/router.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,11 @@ export const sourcesRoute = {
element: <SourcesModal currentView="sources" />
}

export const channelsRoute = {
path: 'channels',
element: <SourcesModal currentView="channels" />
}

export const utmMediumsRoute = {
path: 'utm_mediums',
element: <SourcesModal currentView="utm_mediums" />
Expand Down Expand Up @@ -195,6 +200,7 @@ export function createAppRouter(site: PlausibleSite) {
children: [
{ index: true, element: <DashboardKeybinds /> },
sourcesRoute,
channelsRoute,
utmMediumsRoute,
utmSourcesRoute,
utmCampaignsRoute,
Expand Down
2 changes: 1 addition & 1 deletion assets/js/dashboard/stats/modals/breakdown-modal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -170,7 +170,7 @@ export default function BreakdownModal<TListItem extends { name: string }>({
<BreakdownTable<TListItem>
title={reportInfo.title}
{...apiState}
onSearch={setSearch}
onSearch={searchEnabled ? setSearch : undefined}
columns={columns}
/>
)
Expand Down
13 changes: 12 additions & 1 deletion assets/js/dashboard/stats/modals/filter-modal.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { useParams } from 'react-router-dom';
import Modal from './modal';
import { EVENT_PROPS_PREFIX, FILTER_GROUP_TO_MODAL_TYPE, formatFilterGroup, FILTER_OPERATIONS, getFilterGroup, FILTER_MODAL_TO_FILTER_GROUP, cleanLabels } from '../../util/filters';
import { useQueryContext } from '../../query-context';
import { useSiteContext } from '../../site-context';
import { isModifierPressed, isTyping } from '../../keybinding';
import FilterModalGroup from "./filter-modal-group";
import { rootRoute } from '../../router';
Expand Down Expand Up @@ -134,6 +135,14 @@ class FilterModal extends React.Component {
})
}

getFilterGroups() {
const groups = FILTER_MODAL_TO_FILTER_GROUP[this.props.modalType]
if (this.props.modalType === 'source' && !this.props.site.flags.channels) {
return groups.filter((group) => group !== 'channel')
}
return groups
}

render() {
return (
<Modal maxWidth="460px">
Expand All @@ -144,7 +153,7 @@ class FilterModal extends React.Component {
<div className="mt-4 border-b border-gray-300"></div>
<main className="modal__content">
<form className="flex flex-col" onSubmit={this.handleSubmit.bind(this)}>
{FILTER_MODAL_TO_FILTER_GROUP[this.props.modalType].map((filterGroup) => (
{this.getFilterGroups().map((filterGroup) => (
<FilterModalGroup
key={filterGroup}
filterGroup={filterGroup}
Expand Down Expand Up @@ -189,12 +198,14 @@ export default function FilterModalWithRouter(props) {
const navigate = useAppNavigate();
const { field } = useParams()
const { query } = useQueryContext()
const site = useSiteContext()
return (
<FilterModal
{...props}
modalType={field || 'page'}
query={query}
navigate={navigate}
site={site}
/>
)
}
3 changes: 3 additions & 0 deletions assets/js/dashboard/stats/modals/sources.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,9 @@ const VIEWS = {
)
}
},
channels: {
info: { title: 'Top Acquisition Channels', dimension: 'channel', endpoint: '/channels', dimensionLabel: 'Channel', defaultOrder: ["visitors", SortDirection.desc] }
},
utm_mediums: {
info: { title: 'Top UTM Mediums', dimension: 'utm_medium', endpoint: '/utm_mediums', dimensionLabel: 'UTM Medium', defaultOrder: ["visitors", SortDirection.desc] }
},
Expand Down
44 changes: 42 additions & 2 deletions assets/js/dashboard/stats/sources/source-list.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import classNames from 'classnames';
import ImportedQueryUnsupportedWarning from '../imported-query-unsupported-warning';
import { useQueryContext } from '../../query-context';
import { useSiteContext } from '../../site-context';
import { sourcesRoute, utmCampaignsRoute, utmContentsRoute, utmMediumsRoute, utmSourcesRoute, utmTermsRoute } from '../../router';
import { sourcesRoute, channelsRoute, utmCampaignsRoute, utmContentsRoute, utmMediumsRoute, utmSourcesRoute, utmTermsRoute } from '../../router';

const UTM_TAGS = {
utm_medium: { label: 'UTM Medium', shortLabel: 'UTM Medium', endpoint: '/utm_mediums' },
Expand Down Expand Up @@ -67,6 +67,41 @@ function AllSources({ afterFetchData }) {
)
}

function Channels({ afterFetchData }) {
const { query } = useQueryContext();
const site = useSiteContext();

function fetchData() {
return api.get(url.apiPath(site, '/channels'), query, { limit: 9 })
}

function getFilterFor(listItem) {
return {
prefix: 'channel',
filter: ["is", "channel", [listItem['name']]]
}
}

function chooseMetrics() {
return [
metrics.createVisitors({ meta: { plot: true } }),
hasGoalFilter(query) && metrics.createConversionRate(),
].filter(metric => !!metric)
}

return (
<ListReport
fetchData={fetchData}
afterFetchData={afterFetchData}
getFilterFor={getFilterFor}
keyLabel="Channel"
metrics={chooseMetrics()}
detailsLinkProps={{ path: channelsRoute.path, search: (search) => search }}
color="bg-blue-50"
/>
)
}

function UTMSources({ tab, afterFetchData }) {
const { query } = useQueryContext();
const site = useSiteContext();
Expand All @@ -79,7 +114,7 @@ function UTMSources({ tab, afterFetchData }) {
utm_content: utmContentsRoute,
utm_term: utmTermsRoute,
}[tab]

function fetchData() {
return api.get(url.apiPath(site, utmTag.endpoint), query, { limit: 9 })
}
Expand Down Expand Up @@ -138,6 +173,9 @@ export default function SourceList() {
return (
<div className="flex text-xs font-medium text-gray-500 dark:text-gray-400 space-x-2">
<div className={currentTab === 'all' ? activeClass : defaultClass} onClick={setTab('all')}>All</div>
{site.flags.channels &&
<div className={currentTab === 'channels' ? activeClass : defaultClass} onClick={setTab('channels')}>Channels</div>
}

<Menu as="div" className="relative inline-block text-left">
<div>
Expand Down Expand Up @@ -187,6 +225,8 @@ export default function SourceList() {
function renderContent() {
if (currentTab === 'all') {
return <AllSources afterFetchData={afterFetchData} />
} else if (currentTab == 'channels') {
return <Channels afterFetchData={afterFetchData} />
} else {
return <UTMSources tab={currentTab} afterFetchData={afterFetchData} />
}
Expand Down
3 changes: 2 additions & 1 deletion assets/js/dashboard/util/filters.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { useQueryContext } from '../query-context'

export const FILTER_MODAL_TO_FILTER_GROUP = {
page: ['page', 'entry_page', 'exit_page'],
source: ['source', 'referrer'],
source: ['source', 'channel', 'referrer'],
location: ['country', 'region', 'city'],
screen: ['screen'],
browser: ['browser', 'browser_version'],
Expand Down Expand Up @@ -273,6 +273,7 @@ export const formattedFilters = {
prop_key: 'Property',
prop_value: 'Value',
source: 'Source',
channel: 'Channel',
utm_medium: 'UTM Medium',
utm_source: 'UTM Source',
utm_campaign: 'UTM Campaign',
Expand Down
Loading

0 comments on commit 32ba14b

Please sign in to comment.