Adds dark mode to entire dashboard (#467)

* Adds New Dark Mode Assets

* Moves triangle for dropdown to a reasonable position

* Majority .eex dark implementation

* Fixes Logo Positioning

* Adds theme flag to user schema, uses it

* Uses correct variables for theme applicator script

* Minor missed theme changes/fallbacks

* Individual Component Support + Theme Context

* Sources Tab Support

This was a pain to test D:

* Partial Stats Sections Support

* More of stats modules supported

* Modal +table support

* Improves some Flatpickr in light theme, supports dark theme

* Fixes missed settings tab colors

* Finishes Devices module support

* Fixes bar graph colors

* Better colorizes maps module

* Undoes colorized bars

(they looked bad, on second thought)

* Fixes loading indicator

* Finishes conversions module

* Adds changelog entry

The PR number could be wrong, will double check

* Fixes missed header color

* Fixes naming of migration and removes static alter

* Does migration correctly

As I said, my Elixir is pretty weak heh

* Adds support for spike notifications setting

* Improves contrast and visibility for email settings

* Resolves @ukutaht's comments on #467

* Fixes missing dark style

* Found one more missed dark element (shared links)

* Formatting fixes
This commit is contained in:
Vignesh Joglekar 2020-12-16 03:57:28 -06:00 committed by GitHub
parent a50fd555cf
commit 425975efec
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
81 changed files with 884 additions and 562 deletions

View File

@ -9,6 +9,7 @@ All notable changes to this project will be documented in this file.
- Display weekday on the visitor graph plausible/analytics#175
- Collect and display browser & OS versions plausible/analytics#397
- Simple notifications around traffic spikes plausible/analytics#453
- Dark theme option/system setting follow plausible/analytics#467
### Changed
- Use alpine as base image to decrease Docker image size plausible/analytics#353

View File

@ -5,6 +5,7 @@
@import "modal.css";
@import "loader.css";
@import "tooltip.css";
@import "flatpickr.dark.css";
.button {
@apply bg-indigo-600 border border-transparent rounded-md py-2 px-4 inline-flex justify-center text-sm leading-5 font-medium text-white transition hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500;
@ -156,7 +157,7 @@ blockquote {
.dropdown-content::before {
top: -16px;
right: 64px;
right: 8px;
left: auto;
}
.dropdown-content::before {
@ -174,7 +175,7 @@ blockquote {
}
.dropdown-content::after {
top: -14px;
right: 65px;
right: 9px;
left: auto;
}
@ -196,6 +197,14 @@ blockquote {
background-color: #f1f5f8;
}
.dark .table-striped tbody tr:nth-child(odd) {
background-color: rgb(37, 47, 63);
}
.dark .table-striped tbody tr:nth-child(even) {
background-color: rgb(26, 32, 44);
}
.twitter-icon {
width: 1.25em;
height: 1.25em;
@ -252,3 +261,8 @@ blockquote {
.datamaps-subunit {
cursor: pointer;
}
/* Only because the map handler doesn't expose an easier way to change the shadow color */
.dark .hoverinfo {
box-shadow: 1px 1px 5px rgb(26, 32, 44);
}

View File

@ -0,0 +1,110 @@
/* Because Flatpickr offers zero support for dynamic theming on its own (outside of third-party plugins) */
.dark .flatpickr-calendar {
background-color: #1f2937;
}
.dark .flatpickr-weekday {
color: #f3f4f6;
}
.dark .flatpickr-prev-month {
fill: #f3f4f6 !important;
}
.dark .flatpickr-next-month {
fill: #f3f4f6 !important;
}
.dark .flatpickr-monthDropdown-months {
color: #f3f4f6 !important;
}
.dark .numInputWrapper {
color: #f3f4f6;
}
.dark .flatpickr-day.prevMonthDay {
color: #94a3af;
}
.dark .flatpickr-day {
color: #E5E7EB;
}
.dark .flatpickr-day.prevMonthDay {
color: #9CA3AF;
}
.dark .flatpickr-day.nextMonthDay {
color: #9CA3AF;
}
.dark .flatpickr-day:hover {
background-color: #374151;
}
.dark :not(.startRange):not(.endRange).flatpickr-day.nextMonthDay:hover {
background-color: #374151;
}
.dark :not(.startRange):not(.endRange).flatpickr-day.prevMonthDay:hover {
background-color: #374151;
}
.dark .flatpickr-next-month {
fill: #f3f4f6;
}
.dark .flatpickr-day.flatpickr-disabled {
color: #4B5563;
}
.dark .flatpickr-day.flatpickr-disabled:hover {
color: #4B5563;
}
.dark .flatpickr-day.today {
background-color: rgba(167, 243, 208, 0.5);
}
.dark .flatpickr-day.today {
border-color: #34D399;
}
.dark .flatpickr-day.inRange {
background-color: #374151;
box-shadow: -5px 0 0 #374151,5px 0 0 #374151;
border-color: #374151;
}
.dark .flatpickr-day.prevMonthDay.inRange {
background-color: #374151;
box-shadow: -5px 0 0 #374151,5px 0 0 #374151;
border-color: #374151;
}
.dark .flatpickr-day.nextMonthDay.inRange {
background-color: #374151;
box-shadow: -5px 0 0 #374151,5px 0 0 #374151;
border-color: #374151;
}
.flatpickr-day.startRange {
background: #6574cd !important;
border-color: #6574cd !important;
}
.flatpickr-day.endRange {
background: #6574cd !important;
border-color: #6574cd !important;
}
.dark .flatpickr-day.selected.startRange + .endRange:not(:nth-child(7n+1)), .flatpickr-day.startRange.startRange + .endRange:not(:nth-child(7n+1)), .flatpickr-day.endRange.startRange + .endRange:not(:nth-child(7n+1)) {
-webkit-box-shadow: -10px 0 0 #4556c3 !important;
box-shadow: -10px 0 0 #4556c3 !important;
}
.flatpickr-day.selected.startRange + .endRange:not(:nth-child(7n+1)), .flatpickr-day.startRange.startRange + .endRange:not(:nth-child(7n+1)), .flatpickr-day.endRange.startRange + .endRange:not(:nth-child(7n+1)) {
-webkit-box-shadow: -10px 0 0 #4556c3 !important;
box-shadow: -10px 0 0 #4556c3 !important;
}

View File

@ -20,6 +20,11 @@
-webkit-animation: spin 1s ease-in-out infinite;
}
.dark .loading div {
border: 3px solid #606f7b;
border-top-color: #dae1e7;
}
.loading.sm div {
width: 25px;
height: 25px;

View File

@ -101,11 +101,11 @@ class DatePicker extends React.Component {
renderArrow(period, prevDate, nextDate) {
return (
<div className="flex rounded shadow bg-white mr-4 cursor-pointer">
<QueryLink to={{date: prevDate}} query={this.props.query} className="flex items-center px-2 border-r border-gray-300">
<div className="flex rounded shadow bg-white dark:bg-gray-800 mr-4 cursor-pointer">
<QueryLink to={{date: prevDate}} query={this.props.query} className="flex items-center px-2 border-r border-gray-300 dark:border-gray-500 dark:text-gray-100">
<svg className="feather h-4 w-4" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><polyline points="15 18 9 12 15 6"></polyline></svg>
</QueryLink>
<QueryLink to={{date: nextDate}} query={this.props.query} className="flex items-center px-2">
<QueryLink to={{date: nextDate}} query={this.props.query} className="flex items-center px-2 dark:text-gray-100">
<svg className="feather h-4 w-4" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><polyline points="9 18 15 12 9 6"></polyline></svg>
</QueryLink>
</div>
@ -135,7 +135,7 @@ class DatePicker extends React.Component {
renderDropDown() {
return (
<div className="relative" style={{height: '35.5px', width: '190px'}} ref={node => this.dropDownNode = node}>
<div onClick={this.open.bind(this)} className="flex items-center justify-between rounded bg-white shadow px-4 pr-3 py-2 leading-tight cursor-pointer text-sm font-medium text-gray-800 h-full">
<div onClick={this.open.bind(this)} className="flex items-center justify-between rounded bg-white dark:bg-gray-800 shadow px-4 pr-3 py-2 leading-tight cursor-pointer text-sm font-medium text-gray-800 dark:text-gray-200 h-full">
<span className="mr-2">{this.timeFrameText()}</span>
<svg className="text-pink-500 h-4 w-4" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
<polyline points="6 9 12 15 18 9"></polyline>
@ -176,7 +176,7 @@ class DatePicker extends React.Component {
if (opts.date) { opts.date = formatISO(opts.date) }
return (
<QueryLink to={{period, ...opts}} onClick={this.close.bind(this)} query={this.props.query} className={boldClass + ' block px-4 py-2 text-sm leading-tight hover:bg-gray-100 hover:text-gray-900'}>
<QueryLink to={{period, ...opts}} onClick={this.close.bind(this)} query={this.props.query} className={boldClass + ' block px-4 py-2 text-sm leading-tight hover:bg-gray-100 dark:hover:bg-gray-900 hover:text-gray-900 dark:hover:text-gray-100'}>
{text}
</QueryLink>
)
@ -186,29 +186,29 @@ class DatePicker extends React.Component {
if (this.state.mode === 'menu') {
return (
<div className="absolute mt-2 rounded shadow-md z-10" style={{width: '235px', right: '-14px'}}>
<div className="rounded bg-white ring-1 ring-black ring-opacity-5 font-medium text-gray-800">
<div className="rounded bg-white dark:bg-gray-800 ring-1 ring-black ring-opacity-5 font-medium text-gray-800 dark:text-gray-200">
<div className="py-1">
{ this.renderLink('day', 'Today') }
{ this.renderLink('realtime', 'Realtime') }
</div>
<div className="border-t border-gray-200"></div>
<div className="border-t border-gray-200 dark:border-gray-500"></div>
<div className="py-1">
{ this.renderLink('7d', 'Last 7 days') }
{ this.renderLink('30d', 'Last 30 days') }
</div>
<div className="border-t border-gray-200"></div>
<div className="border-t border-gray-200 dark:border-gray-500"></div>
<div className="py-1">
{ this.renderLink('month', 'This month') }
{ this.renderLink('month', 'Last month', {date: lastMonth(this.props.site)}) }
</div>
<div className="border-t border-gray-200"></div>
<div className="border-t border-gray-200 dark:border-gray-500"></div>
<div className="py-1">
{ this.renderLink('6mo', 'Last 6 months') }
{ this.renderLink('12mo', 'Last 12 months') }
</div>
<div className="border-t border-gray-200"></div>
<div className="border-t border-gray-200 dark:border-gray-500"></div>
<div className="py-1">
<span onClick={e => this.setState({mode: 'calendar'}, this.openCalendar.bind(this))} className="block px-4 py-2 text-sm leading-tight hover:bg-gray-100 hover:text-gray-900 cursor-pointer">Custom range</span>
<span onClick={e => this.setState({mode: 'calendar'}, this.openCalendar.bind(this))} className="block px-4 py-2 text-sm leading-tight hover:bg-gray-100 dark:hover:bg-gray-900 hover:text-gray-900 dark:hover:text-gray-100 cursor-pointer">Custom range</span>
</div>
</div>
</div>

View File

@ -14,7 +14,7 @@ export default class ErrorBoundary extends React.Component {
render() {
if (this.state.error) {
return (
<div className="text-center text-gray-900 mt-36">
<div className="text-center text-gray-900 dark:text-gray-100 mt-36">
<RocketIcon />
<div className="text-lg font-bold">Oops! Something went wrong</div>
<div className="text-lg">{this.state.error.name + ': ' + this.state.error.message}</div>

View File

@ -68,7 +68,7 @@ function renderFilter(history, [key, value], query) {
}
return (
<span key={key} title={value} className="inline-flex bg-white text-gray-700 shadow text-sm rounded py-2 px-3 mr-4">
<span key={key} title={value} className="inline-flex bg-white dark:bg-gray-800 text-gray-700 dark:text-gray-300 shadow text-sm rounded py-2 px-3 mr-4">
{filterText(key, value, query)} <b className="ml-1 cursor-pointer" onClick={removeFilter}></b>
</span>
)

View File

@ -5,7 +5,7 @@ import Historical from './historical'
import Realtime from './realtime'
import {parseQuery} from './query'
import * as api from './api'
import { withThemeProvider } from './theme-provider-hoc';
const THIRTY_SECONDS = 30000
@ -51,4 +51,4 @@ class Dashboard extends React.Component {
}
}
export default withRouter(Dashboard)
export default withRouter(withThemeProvider(Dashboard))

View File

@ -45,9 +45,9 @@ export default class SiteSwitcher extends React.Component {
}
renderSiteLink(domain) {
const extraClass = domain === this.props.site.domain ? 'font-medium text-gray-900' : 'hover:bg-gray-100 hover:text-gray-900 focus:outline-none focus:bg-gray-100 focus:text-gray-900'
const extraClass = domain === this.props.site.domain ? 'font-medium text-gray-900 dark:text-gray-100' : 'hover:bg-gray-100 dark:hover:bg-gray-900 hover:text-gray-900 dark:hover:text-gray-100 focus:outline-none focus:bg-gray-100 dark:focus:bg-gray-900 focus:text-gray-900 dark:focus:text-gray-100'
return (
<a href={`/${encodeURIComponent(domain)}`} key={domain} className={`block truncate px-4 py-2 text-sm leading-5 text-gray-700 ${extraClass}`}>
<a href={`/${encodeURIComponent(domain)}`} key={domain} className={`block truncate px-4 py-2 text-sm leading-5 text-gray-700 dark:text-gray-300 ${extraClass}`}>
<img src={`https://icons.duckduckgo.com/ip3/${domain}.ico`} referrerPolicy="no-referrer" className="inline w-4 mr-2 align-middle" />
<span>{domain}</span>
</a>
@ -58,17 +58,17 @@ export default class SiteSwitcher extends React.Component {
if (this.state.loading) {
return <div className="px-4 py-6"><div className="loading sm mx-auto"><div></div></div></div>
} else if (this.state.error) {
return <div className="mx-auto px-4 py-6">Something went wrong, try again</div>
return <div className="mx-auto px-4 py-6 dark:text-gray-100">Something went wrong, try again</div>
} else {
return (
<React.Fragment>
<div className="py-1">
<a href={`/${encodeURIComponent(this.props.site.domain)}/settings`} className="group flex items-center px-4 py-2 text-sm leading-5 text-gray-700 hover:bg-gray-100 hover:text-gray-900 focus:outline-none focus:bg-gray-100 focus:text-gray-900" role="menuitem">
<svg viewBox="0 0 20 20" fill="currentColor" className="mr-2 h-4 w-4 text-gray-500 group-hover:text-gray-600 group-focus:text-gray-500"><path d="M5 4a1 1 0 00-2 0v7.268a2 2 0 000 3.464V16a1 1 0 102 0v-1.268a2 2 0 000-3.464V4zM11 4a1 1 0 10-2 0v1.268a2 2 0 000 3.464V16a1 1 0 102 0V8.732a2 2 0 000-3.464V4zM16 3a1 1 0 011 1v7.268a2 2 0 010 3.464V16a1 1 0 11-2 0v-1.268a2 2 0 010-3.464V4a1 1 0 011-1z" /></svg>
<a href={`/${encodeURIComponent(this.props.site.domain)}/settings`} className="group flex items-center px-4 py-2 text-sm leading-5 text-gray-700 dark:text-gray-300 hover:bg-gray-100 dark:hover:bg-gray-900 hover:text-gray-900 dark:hover:text-gray-100 focus:outline-none focus:bg-gray-100 dark:focus:bg-gray-900 focus:text-gray-900 dark:focus:text-gray-100" role="menuitem">
<svg viewBox="0 0 20 20" fill="currentColor" className="mr-2 h-4 w-4 text-gray-500 dark:text-gray-200 group-hover:text-gray-600 dark:group-hover:text-gray-400 group-focus:text-gray-500 dark:group-focus:text-gray-200"><path d="M5 4a1 1 0 00-2 0v7.268a2 2 0 000 3.464V16a1 1 0 102 0v-1.268a2 2 0 000-3.464V4zM11 4a1 1 0 10-2 0v1.268a2 2 0 000 3.464V16a1 1 0 102 0V8.732a2 2 0 000-3.464V4zM16 3a1 1 0 011 1v7.268a2 2 0 010 3.464V16a1 1 0 11-2 0v-1.268a2 2 0 010-3.464V4a1 1 0 011-1z" /></svg>
Site settings
</a>
</div>
<div className="border-t border-gray-100"></div>
<div className="border-t border-gray-100 dark:border-gray-900"></div>
<div className="py-1">
{ this.state.sites.map(this.renderSiteLink.bind(this)) }
</div>
@ -88,11 +88,11 @@ export default class SiteSwitcher extends React.Component {
}
render() {
const hoverClass = this.props.loggedIn ? 'hover:text-gray-500 focus:border-blue-300 focus:ring ' : 'cursor-default'
const hoverClass = this.props.loggedIn ? 'hover:text-gray-500 dark:hover:text-gray-200 focus:border-blue-300 focus:ring ' : 'cursor-default'
return (
<div className="relative inline-block text-left z-10 mr-8">
<button onClick={this.toggle.bind(this)} className={`inline-flex items-center text-lg w-full rounded-md py-2 leading-5 font-bold text-gray-700 focus:outline-none transition ease-in-out duration-150 ${hoverClass}`}>
<button onClick={this.toggle.bind(this)} className={`inline-flex items-center text-lg w-full rounded-md py-2 leading-5 font-bold text-gray-700 dark:text-gray-300 focus:outline-none transition ease-in-out duration-150 ${hoverClass}`}>
<img src={`https://icons.duckduckgo.com/ip3/${this.props.site.domain}.ico`} referrerPolicy="no-referrer" className="inline w-4 mr-2 align-middle" />
{this.props.site.domain}
@ -109,7 +109,7 @@ export default class SiteSwitcher extends React.Component {
leaveTo="opacity-0 scale-95"
>
<div className="origin-top-left absolute left-0 mt-2 w-64 rounded-md shadow-lg" ref={node => this.dropDownNode = node} >
<div className="rounded-md bg-white ring-1 ring-black ring-opacity-5">
<div className="rounded-md bg-white dark:bg-gray-800 ring-1 ring-black ring-opacity-5">
{ this.renderDropdown() }
</div>
</div>

View File

@ -50,11 +50,11 @@ export default class Conversions extends React.Component {
return (
<div className="my-2 text-sm" key={goal.name}>
<div className="flex items-center justify-between my-2">
<div className="w-full h-8 relative" style={{maxWidth: 'calc(100% - 16rem)'}}>
<Bar count={goal.count} all={this.state.goals} bg="bg-red-50" />
<div className="w-full h-8 relative dark:text-gray-300" style={{maxWidth: 'calc(100% - 16rem)'}}>
<Bar count={goal.count} all={this.state.goals} bg="bg-red-50 dark:bg-gray-500 dark:bg-opacity-15" />
{this.renderGoalText(goal.name)}
</div>
<div>
<div className="dark:text-gray-200">
<span className="font-medium inline-block w-20 text-right">{numberFormatter(goal.count)}</span>
<span className="font-medium inline-block w-20 text-right">{numberFormatter(goal.total_count)}</span>
<span className="font-medium inline-block w-20 text-right">{goal.conversion_rate}%</span>
@ -68,15 +68,15 @@ export default class Conversions extends React.Component {
render() {
if (this.state.loading) {
return (
<div className="w-full bg-white shadow-xl rounded p-4" style={{height: '94px'}}>
<div className="w-full bg-white dark:bg-gray-825 shadow-xl rounded p-4" style={{height: '94px'}}>
<div className="loading my-2 mx-auto"><div></div></div>
</div>
)
} else if (this.state.goals) {
return (
<div className="w-full bg-white shadow-xl rounded p-4">
<h3 className="font-bold">{this.props.title || "Goal Conversions"}</h3>
<div className="flex items-center mt-3 mb-2 justify-between text-gray-500 text-xs font-bold tracking-wide">
<div className="w-full bg-white dark:bg-gray-825 shadow-xl rounded p-4">
<h3 className="font-bold dark:text-gray-100">{this.props.title || "Goal Conversions"}</h3>
<div className="flex items-center mt-3 mb-2 justify-between text-gray-500 dark:text-gray-400 text-xs font-bold tracking-wide">
<span>Goal</span>
<div className="text-right">
<span className="inline-block w-20">Uniques</span>

View File

@ -39,7 +39,7 @@ export default class PropertyBreakdown extends React.Component {
if (value.is_url) {
return (
<a target="_blank" href={value.name} className="hidden group-hover:block">
<svg className="inline h-4 w-4 ml-1 -mt-1 text-gray-600" fill="currentColor" viewBox="0 0 20 20"><path d="M11 3a1 1 0 100 2h2.586l-6.293 6.293a1 1 0 101.414 1.414L15 6.414V9a1 1 0 102 0V4a1 1 0 00-1-1h-5z"></path><path d="M5 5a2 2 0 00-2 2v8a2 2 0 002 2h8a2 2 0 002-2v-3a1 1 0 10-2 0v3H5V7h3a1 1 0 000-2H5z"></path></svg>
<svg className="inline h-4 w-4 ml-1 -mt-1 text-gray-600 dark:text-gray-400" fill="currentColor" viewBox="0 0 20 20"><path d="M11 3a1 1 0 100 2h2.586l-6.293 6.293a1 1 0 101.414 1.414L15 6.414V9a1 1 0 102 0V4a1 1 0 00-1-1h-5z"></path><path d="M5 5a2 2 0 00-2 2v8a2 2 0 002 2h8a2 2 0 002-2v-3a1 1 0 10-2 0v3H5V7h3a1 1 0 000-2H5z"></path></svg>
</a>
)
}
@ -52,15 +52,15 @@ export default class PropertyBreakdown extends React.Component {
return (
<div className="flex items-center justify-between my-2" key={value.name}>
<div className="w-full h-8 relative" style={{maxWidth: 'calc(100% - 16rem)'}}>
<Bar count={value.count} all={this.state.breakdown} bg="bg-red-50" />
<span className="flex px-2 group" style={{marginTop: '-26px'}}>
<Bar count={value.count} all={this.state.breakdown} bg="bg-red-50 dark:bg-gray-500 dark:bg-opacity-15" />
<span className="flex px-2 group dark:text-gray-300" style={{marginTop: '-26px'}}>
<Link to={{pathname: window.location.pathname, search: query.toString()}} className="hover:underline block truncate">
{ value.name }
</Link>
{ this.renderUrl(value) }
</span>
</div>
<div>
<div className="dark:text-gray-200">
<span className="font-medium inline-block w-20 text-right">{numberFormatter(value.count)}</span>
<span className="font-medium inline-block w-20 text-right">{numberFormatter(value.total_count)}</span>
<span className="font-medium inline-block w-20 text-right">{numberFormatter(value.conversion_rate)}%</span>
@ -86,9 +86,9 @@ export default class PropertyBreakdown extends React.Component {
const isActive = this.state.propKey === key
if (isActive) {
return <li key={key} className="inline-block h-5 text-indigo-700 font-bold border-b-2 border-indigo-700">{key}</li>
return <li key={key} className="inline-block h-5 text-indigo-700 dark:text-indigo-500 font-bold border-b-2 border-indigo-700 dark:border-indigo-500 ">{key}</li>
} else {
return <li key={key} className="hover:text-indigo-700 cursor-pointer" onClick={this.changePropKey.bind(this, key)}>{key}</li>
return <li key={key} className="hover:text-indigo-600 cursor-pointer" onClick={this.changePropKey.bind(this, key)}>{key}</li>
}
}
@ -96,8 +96,8 @@ export default class PropertyBreakdown extends React.Component {
return (
<div className="w-full pl-6 mt-4">
<div className="flex items-center pb-1">
<span className="text-xs font-bold text-gray-600">Breakdown by:</span>
<ul className="flex font-medium text-xs text-gray-500 space-x-2 leading-5 pl-1">
<span className="text-xs font-bold text-gray-600 dark:text-gray-300">Breakdown by:</span>
<ul className="flex font-medium text-xs text-gray-500 dark:text-gray-400 space-x-2 leading-5 pl-1">
{ this.props.goal.prop_names.map(this.renderPill.bind(this)) }
</ul>
</div>

View File

@ -8,11 +8,13 @@ import Bar from './bar'
import MoreLink from './more-link'
import * as api from '../api'
import { navigateToQuery } from '../query'
import { withThemeConsumer } from '../theme-consumer-hoc';
class Countries extends React.Component {
constructor(props) {
super(props)
this.resizeMap = this.resizeMap.bind(this)
this.drawMap = this.drawMap.bind(this)
this.getDataset = this.getDataset.bind(this)
this.state = {loading: true}
}
@ -31,6 +33,14 @@ class Countries extends React.Component {
this.setState({loading: true, countries: null})
this.fetchCountries().then(this.drawMap.bind(this))
}
if (this.props.darkTheme !== prevProps.darkTheme) {
if (document.getElementById('map-container')) {
document.getElementById('map-container').removeChild(document.querySelector('.datamaps-hoverover'));
document.getElementById('map-container').removeChild(document.querySelector('.datamap'));
}
this.drawMap();
}
}
getDataset() {
@ -41,7 +51,10 @@ class Countries extends React.Component {
var paletteScale = d3.scale.linear()
.domain([0,maxValue])
.range(["#f3ebff","#a779e9"]);
.range([
this.props.darkTheme ? "#2e3954" : "#f3ebff",
this.props.darkTheme ? "#6366f1" : "#a779e9"
]);
this.state.countries.forEach(function(item){
dataset[item.name] = {numberOfThings: item.count, fillColor: paletteScale(item.count)};
@ -68,26 +81,30 @@ class Countries extends React.Component {
drawMap() {
var dataset = this.getDataset();
const label = this.props.query.period === 'realtime' ? 'Current visitors' : 'Visitors'
const defaultFill = this.props.darkTheme ? '#2d3747' : '#f8fafc'
const highlightFill = this.props.darkTheme ? '#374151' : '#F5F5F5'
const borderColor = this.props.darkTheme ? '#1f2937' : '#dae1e7'
const highlightBorderColor = this.props.darkTheme ? '#4f46e5' : '#a779e9'
this.map = new Datamap({
element: document.getElementById('map-container'),
responsive: true,
projection: 'mercator',
fills: { defaultFill: '#f8fafc' },
fills: { defaultFill },
data: dataset,
geographyConfig: {
borderColor: '#dae1e7',
borderColor,
highlightBorderWidth: 2,
highlightFillColor: function(geo) {
return geo['fillColor'] || '#F5F5F5';
return geo['fillColor'] || highlightFill;
},
highlightBorderColor: '#a779e9',
highlightBorderColor,
popupTemplate: function(geo, data) {
if (!data) { return ; }
const pluralizedLabel = data.numberOfThings === 1 ? label.slice(0, -1) : label
return ['<div class="hoverinfo">',
return ['<div class="hoverinfo dark:bg-gray-800 dark:shadow-gray-850 dark:border-gray-850 dark:text-gray-200">',
'<strong>', geo.properties.name, '</strong>',
'<br><strong>', numberFormatter(data.numberOfThings), '</strong> ' + pluralizedLabel,
'<br><strong class="dark:text-indigo-400">', numberFormatter(data.numberOfThings), '</strong> ' + pluralizedLabel,
'</div>'].join('');
}
},
@ -109,7 +126,7 @@ class Countries extends React.Component {
if (this.state.countries) {
return (
<React.Fragment>
<h3 className="font-bold">Countries</h3>
<h3 className="font-bold dark:text-gray-100">Countries</h3>
<div className="mt-6 mx-auto" style={{width: '100%', maxWidth: '475px', height: '320px'}} id="map-container"></div>
<MoreLink site={this.props.site} list={this.state.countries} endpoint="countries" />
</React.Fragment>
@ -119,7 +136,7 @@ class Countries extends React.Component {
render() {
return (
<div className="stats-item relative bg-white shadow-xl rounded p-4" style={{height: '436px'}}>
<div className="stats-item relative bg-white dark:bg-gray-825 shadow-xl rounded p-4" style={{height: '436px'}}>
{ this.state.loading && <div className="loading my-32 mx-auto"><div></div></div> }
<FadeIn show={!this.state.loading}>
{ this.renderBody() }
@ -129,4 +146,4 @@ class Countries extends React.Component {
}
}
export default withRouter(Countries)
export default withRouter(withThemeConsumer(Countries))

View File

@ -25,7 +25,7 @@ export default class CurrentVisitors extends React.Component {
const { currentVisitors } = this.state;
if (currentVisitors !== null) {
return (
<Link to={`/${encodeURIComponent(this.props.site.domain)}?period=realtime`} className="block text-sm font-bold text-gray-500">
<Link to={`/${encodeURIComponent(this.props.site.domain)}?period=realtime`} className="block text-sm font-bold text-gray-500 dark:text-gray-300">
<svg className="w-2 mr-2 fill-current text-green-500 inline" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg">
<circle cx="8" cy="8" r="8"/>
</svg>

View File

@ -45,14 +45,14 @@ export default class Browsers extends React.Component {
return (
<div className="flex items-center justify-between my-1 text-sm" key={browser.name}>
<div className="w-full h-8" style={{maxWidth: 'calc(100% - 6rem)'}}>
<Bar count={browser.count} all={this.state.browsers} bg="bg-green-50" />
<span className="flex px-2" style={{marginTop: '-26px'}} >
<Bar count={browser.count} all={this.state.browsers} bg="bg-green-50 dark:bg-gray-500 dark:bg-opacity-15" />
<span className="flex px-2 dark:text-gray-300" style={{marginTop: '-26px'}} >
<Link className="block truncate hover:underline" to={{search: query.toString()}}>
{browser.name}
</Link>
</span>
</div>
<span className="font-medium">{numberFormatter(browser.count)} <span className="inline-block text-xs w-8 text-right">({browser.percentage}%)</span></span>
<span className="font-medium dark:text-gray-200">{numberFormatter(browser.count)} <span className="inline-block text-xs w-8 text-right">({browser.percentage}%)</span></span>
</div>
)
}
@ -68,7 +68,7 @@ export default class Browsers extends React.Component {
if (this.state.browsers && this.state.browsers.length > 0) {
return (
<React.Fragment>
<div className="flex items-center mt-3 mb-2 justify-between text-gray-500 text-xs font-bold tracking-wide">
<div className="flex items-center mt-3 mb-2 justify-between text-gray-500 dark:text-gray-400 text-xs font-bold tracking-wide">
<span>{ key }</span>
<span>{ this.label() }</span>
</div>
@ -76,7 +76,7 @@ export default class Browsers extends React.Component {
</React.Fragment>
)
} else {
return <div className="text-center mt-44 font-medium text-gray-500">No data yet</div>
return <div className="text-center mt-44 font-medium text-gray-500 dark:text-gray-400">No data yet</div>
}
}

View File

@ -67,14 +67,14 @@ class ScreenSizes extends React.Component {
return (
<div className="flex items-center justify-between my-1 text-sm" key={size.name}>
<div className="w-full h-8" style={{maxWidth: 'calc(100% - 6rem)'}}>
<Bar count={size.count} all={this.state.sizes} bg="bg-green-50" />
<span tooltip={EXPLANATION[size.name]} className="flex px-2" style={{marginTop: '-26px'}} >
<Bar count={size.count} all={this.state.sizes} bg="bg-green-50 dark:bg-gray-500 dark:bg-opacity-15" />
<span tooltip={EXPLANATION[size.name]} className="flex px-2 dark:text-gray-300" style={{marginTop: '-26px'}} >
<Link className="block truncate hover:underline" to={{search: query.toString()}}>
{iconFor(size.name)} {size.name}
</Link>
</span>
</div>
<span className="font-medium">{numberFormatter(size.count)} <span className="inline-block text-xs w-8 text-right">({size.percentage}%)</span></span>
<span className="font-medium dark:text-gray-200">{numberFormatter(size.count)} <span className="inline-block text-xs w-8 text-right">({size.percentage}%)</span></span>
</div>
)
}
@ -95,7 +95,7 @@ class ScreenSizes extends React.Component {
</React.Fragment>
)
} else {
return <div className="text-center mt-44 font-medium text-gray-500">No data yet</div>
return <div className="text-center mt-44 font-medium text-gray-500 dark:text-gray-400">No data yet</div>
}
}
@ -142,21 +142,21 @@ export default class Devices extends React.Component {
const isActive = this.state.mode === mode
if (isActive) {
return <li className="inline-block h-5 text-indigo-700 font-bold border-b-2 border-indigo-700">{name}</li>
return <li className="inline-block h-5 text-indigo-700 dark:text-indigo-500 font-bold border-b-2 border-indigo-700 dark:border-indigo-500">{name}</li>
} else {
return <li className="hover:text-indigo-700 cursor-pointer" onClick={this.setMode(mode)}>{name}</li>
return <li className="hover:text-indigo-600 cursor-pointer" onClick={this.setMode(mode)}>{name}</li>
}
}
render() {
return (
<div className="stats-item">
<div className="bg-white shadow-xl rounded p-4 relative" style={{height: '436px'}}>
<div className="bg-white dark:bg-gray-825 shadow-xl rounded p-4 relative" style={{height: '436px'}}>
<div className="w-full flex justify-between">
<h3 className="font-bold">Devices</h3>
<h3 className="font-bold dark:text-gray-100">Devices</h3>
<ul className="flex font-medium text-xs text-gray-500 space-x-2">
<ul className="flex font-medium text-xs text-gray-500 dark:text-gray-400 space-x-2">
{ this.renderPill('Size', 'size') }
{ this.renderPill('Browser', 'browser') }
{ this.renderPill('OS', 'os') }

View File

@ -45,14 +45,14 @@ export default class OperatingSystems extends React.Component {
return (
<div className="flex items-center justify-between my-1 text-sm" key={os.name}>
<div className="w-full h-8" style={{maxWidth: 'calc(100% - 6rem)'}}>
<Bar count={os.count} all={this.state.operatingSystems} bg="bg-green-50" />
<span className="flex px-2" style={{marginTop: '-26px'}}>
<Bar count={os.count} all={this.state.operatingSystems} bg="bg-green-50 dark:gray-500 dark:bg-opacity-15" />
<span className="flex px-2 dark:text-gray-300" style={{marginTop: '-26px'}}>
<Link className="block truncate hover:underline" to={{search: query.toString()}}>
{os.name}
</Link>
</span>
</div>
<span className="font-medium">{numberFormatter(os.count)} <span className="inline-block text-xs w-8 text-right">({os.percentage}%)</span></span>
<span className="font-medium dark:text-gray-200">{numberFormatter(os.count)} <span className="inline-block text-xs w-8 text-right">({os.percentage}%)</span></span>
</div>
)
}
@ -67,7 +67,7 @@ export default class OperatingSystems extends React.Component {
if (this.state.operatingSystems && this.state.operatingSystems.length > 0) {
return (
<React.Fragment>
<div className="flex items-center mt-3 mb-2 justify-between text-gray-500 text-xs font-bold tracking-wide">
<div className="flex items-center mt-3 mb-2 justify-between text-gray-500 dark:text-gray-400 text-xs font-bold tracking-wide">
<span>{ key }</span>
<span>{ this.label() }</span>
</div>
@ -75,7 +75,7 @@ export default class OperatingSystems extends React.Component {
</React.Fragment>
)
} else {
return <div className="text-center mt-44 font-medium text-gray-500">No data yet</div>
return <div className="text-center mt-44 font-medium text-gray-500 dark:text-gray-400">No data yet</div>
}
}

View File

@ -26,7 +26,7 @@ class CountriesModal extends React.Component {
query.set('country', country.name)
return (
<tr className="text-sm" key={country.name}>
<tr className="text-sm dark:text-gray-200" key={country.name}>
<td className="p-2">
<Link className="hover:underline" to={{search: query.toString(), pathname: '/' + encodeURIComponent(this.props.site.domain)}}>
{country.full_country_name}
@ -51,15 +51,15 @@ class CountriesModal extends React.Component {
} else if (this.state.countries) {
return (
<React.Fragment>
<h1 className="text-xl font-bold">Top countries</h1>
<h1 className="text-xl font-bold dark:text-gray-100">Top countries</h1>
<div className="my-4 border-b border-gray-300"></div>
<div className="my-4 border-b border-gray-300 dark:border-gray-500"></div>
<main className="modal__content">
<table className="w-full table-striped table-fixed">
<thead>
<tr>
<th className="p-2 text-xs tracking-wide font-bold text-gray-500" align="left">Country</th>
<th className="p-2 w-32 text-xs tracking-wide font-bold text-gray-500" align="right">{this.label()}</th>
<th className="p-2 text-xs tracking-wide font-bold text-gray-500 dark:text-gray-400" align="left">Country</th>
<th className="p-2 w-32 text-xs tracking-wide font-bold text-gray-500 dark:text-gray-400" align="right">{this.label()}</th>
</tr>
</thead>
<tbody>

View File

@ -43,7 +43,7 @@ class GoogleKeywordsModal extends React.Component {
return (
<React.Fragment key={term.name}>
<tr className="text-sm" key={term.name}>
<tr className="text-sm dark:text-gray-200" key={term.name}>
<td className="p-2 truncate">{term.name}</td>
<td className="p-2 w-32 font-medium" align="right">{numberFormatter(term.count)}</td>
</tr>
@ -54,7 +54,7 @@ class GoogleKeywordsModal extends React.Component {
renderKeywords() {
if (this.state.query.filters.goal) {
return (
<div className="text-center text-gray-700 mt-6">
<div className="text-center text-gray-700 dark:text-gray-300 mt-6">
<RocketIcon />
<div className="text-lg">Sorry, we cannot show which keywords converted best for goal <b>{this.state.query.filters.goal}</b></div>
<div className="text-lg">Google does not share this information</div>
@ -63,7 +63,7 @@ class GoogleKeywordsModal extends React.Component {
} else if (this.state.notConfigured) {
if (this.state.isOwner) {
return (
<div className="text-center text-gray-700 mt-6">
<div className="text-center text-gray-700 dark:text-gray-300 mt-6">
<RocketIcon />
<div className="text-lg">The site is not connected to Google Search Keywords</div>
<div className="text-lg">Configure the integration to view search terms</div>
@ -72,7 +72,7 @@ class GoogleKeywordsModal extends React.Component {
)
} else {
return (
<div className="text-center text-gray-700 mt-6">
<div className="text-center text-gray-700 dark:text-gray-300 mt-6">
<RocketIcon />
<div className="text-lg">The site is not connected to Google Search Kewyords</div>
<div className="text-lg">Cannot show search terms</div>
@ -84,8 +84,8 @@ class GoogleKeywordsModal extends React.Component {
<table className="w-full table-striped table-fixed">
<thead>
<tr>
<th className="p-2 text-xs tracking-wide font-bold text-gray-500" align="left">Search Term</th>
<th className="p-2 w-32 text-xs tracking-wide font-bold text-gray-500" align="right">Visitors</th>
<th className="p-2 text-xs tracking-wide font-bold text-gray-500 dark:text-gray-400" align="left">Search Term</th>
<th className="p-2 w-32 text-xs tracking-wide font-bold text-gray-500 dark:text-gray-400" align="right">Visitors</th>
</tr>
</thead>
<tbody>
@ -95,7 +95,7 @@ class GoogleKeywordsModal extends React.Component {
)
} else {
return (
<div className="text-center text-gray-700 mt-6">
<div className="text-center text-gray-700 dark:text-gray-300 mt-6">
<RocketIcon />
<div className="text-lg">Could not find any search terms for this period</div>
</div>
@ -106,7 +106,7 @@ class GoogleKeywordsModal extends React.Component {
renderGoalText() {
if (this.state.query.filters.goal) {
return (
<h1 className="text-xl font-semibold text-gray-500 leading-none">completed {this.state.query.filters.goal}</h1>
<h1 className="text-xl font-semibold text-gray-500 dark:text-gray-200 leading-none">completed {this.state.query.filters.goal}</h1>
)
}
}
@ -119,11 +119,11 @@ class GoogleKeywordsModal extends React.Component {
} else {
return (
<React.Fragment>
<Link to={`/${encodeURIComponent(this.props.site.domain)}/referrers${window.location.search}`} className="font-bold text-gray-700 hover:underline"> All referrers</Link>
<Link to={`/${encodeURIComponent(this.props.site.domain)}/referrers${window.location.search}`} className="font-bold text-gray-700 dark:text-gray-200 hover:underline"> All referrers</Link>
<div className="my-4 border-b border-gray-300"></div>
<div className="my-4 border-b border-gray-300 dark:border-gray-500"></div>
<main className="modal__content">
<h1 className="text-xl font-semibold mb-0 leading-none">
<h1 className="text-xl font-semibold mb-0 leading-none dark:text-gray-200">
{this.state.totalVisitors} visitors from Google<br />
{toHuman(this.state.query)}
</h1>

View File

@ -47,7 +47,7 @@ class Modal extends React.Component {
<div className="modal is-open" onClick={this.props.onClick}>
<div className="modal__overlay">
<button className="modal__close"></button>
<div ref={this.node} className="modal__container">
<div ref={this.node} className="modal__container dark:bg-gray-800">
{this.props.children}
</div>

View File

@ -51,7 +51,7 @@ class PagesModal extends React.Component {
query.set('page', page.name)
return (
<tr className="text-sm" key={page.name}>
<tr className="text-sm dark:text-gray-200" key={page.name}>
<td className="p-2">
<Link to={{pathname: `/${encodeURIComponent(this.props.site.domain)}`, search: query.toString()}} className="hover:underline">{page.name}</Link>
</td>
@ -79,17 +79,17 @@ class PagesModal extends React.Component {
} else if (this.state.pages) {
return (
<React.Fragment>
<h1 className="text-xl font-bold">{this.title()}</h1>
<h1 className="text-xl font-bold dark:text-gray-100">{this.title()}</h1>
<div className="my-4 border-b border-gray-300"></div>
<main className="modal__content">
<table className="w-full table-striped table-fixed">
<thead>
<tr>
<th className="p-2 text-xs tracking-wide font-bold text-gray-500" align="left">Page url</th>
<th className="p-2 w-32 text-xs tracking-wide font-bold text-gray-500" align="right">{ this.label() }</th>
{this.showPageviews() && <th className="p-2 w-32 text-xs tracking-wide font-bold text-gray-500" align="right">Pageviews</th>}
{this.showBounceRate() && <th className="p-2 w-32 text-xs tracking-wide font-bold text-gray-500" align="right">Bounce rate</th>}
<th className="p-2 text-xs tracking-wide font-bold text-gray-500 dark:text-gray-400" align="left">Page url</th>
<th className="p-2 w-32 text-xs tracking-wide font-bold text-gray-500 dark:text-gray-400" align="right">{ this.label() }</th>
{this.showPageviews() && <th className="p-2 w-32 text-xs tracking-wide font-bold text-gray-500 dark:text-gray-400" align="right">Pageviews</th>}
{this.showBounceRate() && <th className="p-2 w-32 text-xs tracking-wide font-bold text-gray-500 dark:text-gray-400" align="right">Bounce rate</th>}
</tr>
</thead>
<tbody>

View File

@ -47,7 +47,7 @@ class ReferrerDrilldownModal extends React.Component {
if (name !== 'Direct / None') {
return (
<a target="_blank" href={'//' + name} className="hidden group-hover:block">
<svg className="inline h-4 w-4 ml-1 -mt-1 text-gray-600" fill="currentColor" viewBox="0 0 20 20"><path d="M11 3a1 1 0 100 2h2.586l-6.293 6.293a1 1 0 101.414 1.414L15 6.414V9a1 1 0 102 0V4a1 1 0 00-1-1h-5z"></path><path d="M5 5a2 2 0 00-2 2v8a2 2 0 002 2h8a2 2 0 002-2v-3a1 1 0 10-2 0v3H5V7h3a1 1 0 000-2H5z"></path></svg>
<svg className="inline h-4 w-4 ml-1 -mt-1 text-gray-600 dark:text-gray-400" fill="currentColor" viewBox="0 0 20 20"><path d="M11 3a1 1 0 100 2h2.586l-6.293 6.293a1 1 0 101.414 1.414L15 6.414V9a1 1 0 102 0V4a1 1 0 00-1-1h-5z"></path><path d="M5 5a2 2 0 00-2 2v8a2 2 0 002 2h8a2 2 0 002-2v-3a1 1 0 10-2 0v3H5V7h3a1 1 0 000-2H5z"></path></svg>
</a>
)
}
@ -60,7 +60,7 @@ class ReferrerDrilldownModal extends React.Component {
return (
<span className="flex group items-center">
<img src={`https://icons.duckduckgo.com/ip3/${referrer.url}.ico`} referrerPolicy="no-referrer" className="h-4 w-4 mr-2 inline" />
<Link className="block truncate hover:underline" to={{search: query.toString(), pathname: '/' + this.props.site.domain}} title={referrer.name}>
<Link className="block truncate hover:underline dark:text-gray-200" to={{search: query.toString(), pathname: '/' + this.props.site.domain}} title={referrer.name}>
{referrer.name}
</Link>
{ this.renderExternalLink(name) }
@ -71,7 +71,7 @@ class ReferrerDrilldownModal extends React.Component {
renderTweet(tweet, index) {
const authorUrl = `https://twitter.com/${tweet.author_handle}`
const tweetUrl = `${authorUrl}/status/${tweet.tweet_id}`
const border = index === 0 ? '' : ' pt-4 border-t border-gray-300'
const border = index === 0 ? '' : ' pt-4 border-t border-gray-300 dark:border-gray-500'
return (
<div key={tweet.tweet_id}>
@ -80,14 +80,14 @@ class ReferrerDrilldownModal extends React.Component {
<img className="rounded-full w-8" src={tweet.author_image} />
<div className="ml-2 leading-tight">
<div className="font-bold group-hover:text-blue-500">{tweet.author_name}</div>
<div className="text-xs text-gray-500">@{tweet.author_handle}</div>
<div className="text-xs text-gray-500 dark:text-gray-400">@{tweet.author_handle}</div>
</div>
</a>
<a className="ml-auto twitter-icon" href={tweetUrl} target="_blank"></a>
</div>
<div className="my-2 cursor-text tweet-text whitespace-pre-wrap" dangerouslySetInnerHTML={{__html: tweet.text}}>
</div>
<div className="text-xs text-gray-700 font-medium">
<div className="text-xs text-gray-700 dark:text-gray-300 font-medium">
{formatFullDate(new Date(tweet.created))}
</div>
</div>
@ -97,13 +97,13 @@ class ReferrerDrilldownModal extends React.Component {
renderReferrer(referrer) {
if (referrer.tweets) {
return (
<tr className="text-sm" key={referrer.name}>
<tr className="text-sm dark:text-gray-200" key={referrer.name}>
<td className="p-2">
{ this.renderReferrerName(referrer) }
<span className="text-gray-500 ml-2 text-xs">
appears in {referrer.tweets.length} tweets
</span>
<div className="my-4 pl-4 border-l-2 border-gray-300">
<div className="my-4 pl-4 border-l-2 border-gray-300 dark:border-gray-500">
{ referrer.tweets.map(this.renderTweet) }
</div>
</td>
@ -114,7 +114,7 @@ class ReferrerDrilldownModal extends React.Component {
)
} else {
return (
<tr className="text-sm" key={referrer.name}>
<tr className="text-sm dark:text-gray-200" key={referrer.name}>
<td className="p-2">
{ this.renderReferrerName(referrer) }
</td>
@ -129,7 +129,7 @@ class ReferrerDrilldownModal extends React.Component {
renderGoalText() {
if (this.state.query.filters.goal) {
return (
<h1 className="text-xl font-semibold text-gray-500 leading-none">completed {this.state.query.filters.goal}</h1>
<h1 className="text-xl font-semibold text-gray-500 dark:text-gray-300 leading-none">completed {this.state.query.filters.goal}</h1>
)
}
}
@ -142,20 +142,20 @@ class ReferrerDrilldownModal extends React.Component {
} else if (this.state.referrers) {
return (
<React.Fragment>
<h1 className="text-xl font-bold">Referrer drilldown</h1>
<h1 className="text-xl font-bold dark:text-gray-100">Referrer drilldown</h1>
<div className="my-4 border-b border-gray-300"></div>
<div className="my-4 border-b border-gray-300 dark:border-gray-500"></div>
<main className="modal__content mt-0">
<h1 className="text-xl font-semibold mb-0 leading-none">{this.state.totalVisitors} visitors from {decodeURIComponent(this.props.match.params.referrer)}<br /> {toHuman(this.state.query)}</h1>
<h1 className="text-xl font-semibold mb-0 leading-none dark:text-gray-200">{this.state.totalVisitors} visitors from {decodeURIComponent(this.props.match.params.referrer)}<br /> {toHuman(this.state.query)}</h1>
{this.renderGoalText()}
<table className="w-full table-striped table-fixed mt-4">
<thead>
<tr>
<th className="p-2 text-xs tracking-wide font-bold text-gray-500" align="left">Referrer</th>
<th className="p-2 w-32 text-xs tracking-wide font-bold text-gray-500" align="right">Visitors</th>
{this.showExtra() && <th className="p-2 w-32 text-xs tracking-wide font-bold text-gray-500" align="right">Bounce rate</th>}
{this.showExtra() && <th className="p-2 w-32 text-xs tracking-wide font-bold text-gray-500" align="right">Visit duration</th>}
<th className="p-2 text-xs tracking-wide font-bold text-gray-500 dark:text-gray-400" align="left">Referrer</th>
<th className="p-2 w-32 text-xs tracking-wide font-bold text-gray-500 dark:text-gray-400" align="right">Visitors</th>
{this.showExtra() && <th className="p-2 w-32 text-xs tracking-wide font-bold text-gray-500 dark:text-gray-400" align="right">Bounce rate</th>}
{this.showExtra() && <th className="p-2 w-32 text-xs tracking-wide font-bold text-gray-500 dark:text-gray-400" align="right">Visit duration</th>}
</tr>
</thead>
<tbody>

View File

@ -84,7 +84,7 @@ class SourcesModal extends React.Component {
if (filter === 'utm_campaigns') query.set('utm_campaign', source.name)
return (
<tr className="text-sm" key={source.name}>
<tr className="text-sm dark:text-gray-200" key={source.name}>
<td className="p-2">
<img src={`https://icons.duckduckgo.com/ip3/${source.url}.ico`} referrerPolicy="no-referrer" className="h-4 w-4 mr-2 align-middle inline" />
<Link className="hover:underline" to={{search: query.toString(), pathname: '/' + encodeURIComponent(this.props.site.domain)}}>{ source.name }</Link>
@ -121,18 +121,18 @@ class SourcesModal extends React.Component {
render() {
return (
<Modal site={this.props.site}>
<h1 className="text-xl font-bold">{this.title()}</h1>
<h1 className="text-xl font-bold dark:text-gray-100">{this.title()}</h1>
<div className="my-4 border-b border-gray-300"></div>
<div className="my-4 border-b border-gray-300 dark:border-gray-500"></div>
<main className="modal__content">
<table className="w-full table-striped table-fixed">
<thead>
<tr>
<th className="p-2 text-xs tracking-wide font-bold text-gray-500" align="left">Source</th>
<th className="p-2 w-32 text-xs tracking-wide font-bold text-gray-500" align="right">{this.label()}</th>
{this.showExtra() && <th className="p-2 w-32 text-xs tracking-wide font-bold text-gray-500" align="right">Bounce rate</th>}
{this.showExtra() && <th className="p-2 w-32 text-xs tracking-wide font-bold text-gray-500" align="right">Visit duration</th>}
<th className="p-2 text-xs tracking-wide font-bold text-gray-500 dark:text-gray-400" align="left">Source</th>
<th className="p-2 w-32 text-xs tracking-wide font-bold text-gray-500 dark:text-gray-400" align="right">{this.label()}</th>
{this.showExtra() && <th className="p-2 w-32 text-xs tracking-wide font-bold text-gray-500 dark:text-gray-400" align="right">Bounce rate</th>}
{this.showExtra() && <th className="p-2 w-32 text-xs tracking-wide font-bold text-gray-500 dark:text-gray-400" align="right">Visit duration</th>}
</tr>
</thead>
<tbody>

View File

@ -5,7 +5,7 @@ export default function MoreLink({site, list, endpoint}) {
if (list.length > 0) {
return (
<div className="text-center w-full absolute bottom-0 left-0 pb-3">
<Link to={`/${encodeURIComponent(site.domain)}/${endpoint}${window.location.search}`} className="leading-snug font-bold text-sm text-gray-500 hover:text-red-500 transition tracking-wide">
<Link to={`/${encodeURIComponent(site.domain)}/${endpoint}${window.location.search}`} className="leading-snug font-bold text-sm text-gray-500 dark:text-gray-400 hover:text-red-500 dark:hover:text-red-400 transition tracking-wide">
<svg className="feather mr-1" style={{marginTop: '-2px'}} xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><path d="M8 3H5a2 2 0 0 0-2 2v3m18 0V5a2 2 0 0 0-2-2h-3m0 18h3a2 2 0 0 0 2-2v-3M3 16v3a2 2 0 0 0 2 2h3"/></svg>
MORE
</Link>

View File

@ -45,15 +45,15 @@ export default class Pages extends React.Component {
return (
<div className="flex items-center justify-between my-1 text-sm" key={page.name}>
<div className="w-full h-8 truncate" style={{maxWidth: 'calc(100% - 4rem)'}}>
<Bar count={page.count} all={this.state.pages} bg="bg-orange-50" />
<span className="flex px-2 group" style={{marginTop: '-26px'}} >
<Bar count={page.count} all={this.state.pages} bg="bg-orange-50 dark:bg-gray-500 dark:bg-opacity-15" />
<span className="flex px-2 group dark:text-gray-300" style={{marginTop: '-26px'}} >
<Link to={{pathname: window.location.pathname, search: query.toString()}} className="block hover:underline">{page.name}</Link>
<a target="_blank" href={'http://' + this.props.site.domain + page.name} className="hidden group-hover:block">
<svg className="inline h-4 w-4 ml-1 -mt-1 text-gray-600" fill="currentColor" viewBox="0 0 20 20"><path d="M11 3a1 1 0 100 2h2.586l-6.293 6.293a1 1 0 101.414 1.414L15 6.414V9a1 1 0 102 0V4a1 1 0 00-1-1h-5z"></path><path d="M5 5a2 2 0 00-2 2v8a2 2 0 002 2h8a2 2 0 002-2v-3a1 1 0 10-2 0v3H5V7h3a1 1 0 000-2H5z"></path></svg>
<svg className="inline h-4 w-4 ml-1 -mt-1 text-gray-600 dark:text-gray-400" fill="currentColor" viewBox="0 0 20 20"><path d="M11 3a1 1 0 100 2h2.586l-6.293 6.293a1 1 0 101.414 1.414L15 6.414V9a1 1 0 102 0V4a1 1 0 00-1-1h-5z"></path><path d="M5 5a2 2 0 00-2 2v8a2 2 0 002 2h8a2 2 0 002-2v-3a1 1 0 10-2 0v3H5V7h3a1 1 0 000-2H5z"></path></svg>
</a>
</span>
</div>
<span className="font-medium">{numberFormatter(page.count)}</span>
<span className="font-medium dark:text-gray-200">{numberFormatter(page.count)}</span>
</div>
)
}
@ -73,7 +73,7 @@ export default class Pages extends React.Component {
if (this.state.pages.length > 0) {
return (
<React.Fragment>
<div className="flex items-center mt-3 mb-2 justify-between text-gray-500 text-xs font-bold tracking-wide">
<div className="flex items-center mt-3 mb-2 justify-between text-gray-500 dark:text-gray-400 text-xs font-bold tracking-wide">
<span>Page url</span>
<span>{ this.label() }</span>
</div>
@ -84,7 +84,7 @@ export default class Pages extends React.Component {
</React.Fragment>
)
} else {
return <div className="text-center mt-44 font-medium text-gray-500">No data yet</div>
return <div className="text-center mt-44 font-medium text-gray-500 dark:text-gray-400">No data yet</div>
}
}
@ -97,7 +97,7 @@ export default class Pages extends React.Component {
if (this.state.pages) {
return (
<React.Fragment>
<h3 className="font-bold">{this.title()}</h3>
<h3 className="font-bold dark:text-gray-100">{this.title()}</h3>
{ this.renderList() }
<MoreLink site={this.props.site} list={this.state.pages} endpoint="pages" />
</React.Fragment>
@ -107,7 +107,7 @@ export default class Pages extends React.Component {
render() {
return (
<div className="stats-item relative bg-white shadow-xl rounded p-4" style={{height: '436px'}}>
<div className="stats-item relative bg-white dark:bg-gray-825 shadow-xl rounded p-4" style={{height: '436px'}}>
{ this.state.loading && <div className="loading mt-44 mx-auto"><div></div></div> }
<FadeIn show={!this.state.loading}>
{ this.renderContent() }

View File

@ -57,7 +57,7 @@ export default class Referrers extends React.Component {
if (this.props.query.filters.source && this.props.query.filters.source !== 'Google' && referrer.name !== 'Direct / None') {
return (
<a target="_blank" href={'//' + referrer.name} className="hidden group-hover:block">
<svg className="inline h-4 w-4 ml-1 -mt-1 text-gray-600" fill="currentColor" viewBox="0 0 20 20"><path d="M11 3a1 1 0 100 2h2.586l-6.293 6.293a1 1 0 101.414 1.414L15 6.414V9a1 1 0 102 0V4a1 1 0 00-1-1h-5z"></path><path d="M5 5a2 2 0 00-2 2v8a2 2 0 002 2h8a2 2 0 002-2v-3a1 1 0 10-2 0v3H5V7h3a1 1 0 000-2H5z"></path></svg>
<svg className="inline h-4 w-4 ml-1 -mt-1 text-gray-600 dark:text-gray-400" fill="currentColor" viewBox="0 0 20 20"><path d="M11 3a1 1 0 100 2h2.586l-6.293 6.293a1 1 0 101.414 1.414L15 6.414V9a1 1 0 102 0V4a1 1 0 00-1-1h-5z"></path><path d="M5 5a2 2 0 00-2 2v8a2 2 0 002 2h8a2 2 0 002-2v-3a1 1 0 10-2 0v3H5V7h3a1 1 0 000-2H5z"></path></svg>
</a>
)
}
@ -71,16 +71,16 @@ export default class Referrers extends React.Component {
return (
<div className="flex items-center justify-between my-1 text-sm" key={referrer.name}>
<div className="w-full h-8" style={{maxWidth: 'calc(100% - 4rem)'}}>
<Bar count={referrer.count} all={this.state.referrers} bg="bg-blue-50" />
<Bar count={referrer.count} all={this.state.referrers} bg="bg-blue-50 dark:bg-gray-500 dark:bg-opacity-15" />
<span className="flex px-2 group" style={{marginTop: '-26px'}} >
<LinkOption className="block truncate" to={{search: query.toString()}} disabled={referrer.name === 'Direct / None'}>
<LinkOption className="block truncate dark:text-gray-300" to={{search: query.toString()}} disabled={referrer.name === 'Direct / None'}>
<img src={`https://icons.duckduckgo.com/ip3/${referrer.url}.ico`} referrerPolicy="no-referrer" className="inline h-4 w-4 mr-2 align-middle -mt-px" />
{ referrer.name }
</LinkOption>
{ this.renderExternalLink(referrer) }
</span>
</div>
<span className="font-medium">{numberFormatter(referrer.count)}</span>
<span className="font-medium dark:text-gray-200">{numberFormatter(referrer.count)}</span>
</div>
)
}
@ -93,7 +93,7 @@ export default class Referrers extends React.Component {
if (this.state.referrers.length > 0) {
return (
<React.Fragment>
<div className="flex items-center mt-3 mb-2 justify-between text-gray-500 text-xs font-bold tracking-wide">
<div className="flex items-center mt-3 mb-2 justify-between text-gray-500 dark:text-gray-400 text-xs font-bold tracking-wide">
<span>Referrer</span>
<span>{ this.label() }</span>
</div>
@ -104,7 +104,7 @@ export default class Referrers extends React.Component {
</React.Fragment>
)
} else {
return <div className="text-center mt-44 font-medium text-gray-500">No data yet</div>
return <div className="text-center mt-44 font-medium text-gray-500 dark:text-gray-400">No data yet</div>
}
}
@ -112,7 +112,7 @@ export default class Referrers extends React.Component {
if (this.state.referrers) {
return (
<React.Fragment>
<h3 className="font-bold">Top Referrers</h3>
<h3 className="font-bold dark:text-gray-100">Top Referrers</h3>
{ this.renderList() }
<MoreLink site={this.props.site} list={this.state.referrers} endpoint={`referrers/${this.props.query.filters.source}`} />
</React.Fragment>
@ -122,7 +122,7 @@ export default class Referrers extends React.Component {
render() {
return (
<div className="stats-item relative bg-white shadow-xl rounded p-4" style={{height: '436px'}}>
<div className="stats-item relative bg-white dark:bg-gray-825 shadow-xl rounded p-4" style={{height: '436px'}}>
{ this.state.loading && <div className="loading mt-44 mx-auto"><div></div></div> }
<FadeIn show={!this.state.loading}>
{ this.renderContent() }

View File

@ -37,14 +37,14 @@ export default class SearchTerms extends React.Component {
return (
<div className="flex items-center justify-between my-1 text-sm" key={term.name}>
<div className="w-full h-8" style={{maxWidth: 'calc(100% - 4rem)'}}>
<Bar count={term.count} all={this.state.searchTerms} bg="bg-blue-50" />
<span className="flex px-2" style={{marginTop: '-26px'}} >
<Bar count={term.count} all={this.state.searchTerms} bg="bg-blue-50 dark:bg-gray-500 dark:bg-opacity-15" />
<span className="flex px-2 dark:text-gray-300" style={{marginTop: '-26px'}} >
<span className="block truncate">
{ term.name }
</span>
</span>
</div>
<span className="font-medium">{numberFormatter(term.count)}</span>
<span className="font-medium dark:text-gray-200">{numberFormatter(term.count)}</span>
</div>
)
}
@ -52,7 +52,7 @@ export default class SearchTerms extends React.Component {
renderList() {
if (this.props.query.filters.goal) {
return (
<div className="text-center text-gray-700 text-sm mt-20">
<div className="text-center text-gray-700 dark:text-gray-300 text-sm mt-20">
<RocketIcon />
<div>Sorry, we cannot show which keywords converted best for goal <b>{this.props.query.filters.goal}</b></div>
<div>Google does not share this information</div>
@ -61,7 +61,7 @@ export default class SearchTerms extends React.Component {
} else if (this.state.notConfigured) {
return (
<div className="text-center text-gray-700 text-sm mt-20">
<div className="text-center text-gray-700 dark:text-gray-300 text-sm mt-20">
<RocketIcon />
<div>The site is not connected to Google Search Keywords</div>
<div>Cannot show search terms</div>
@ -73,7 +73,7 @@ export default class SearchTerms extends React.Component {
return (
<React.Fragment>
<div className="flex items-center mt-3 mb-2 justify-between text-gray-500 text-xs font-bold tracking-wide">
<div className="flex items-center mt-3 mb-2 justify-between text-gray-500 dark:text-gray-400 text-xs font-bold tracking-wide">
<span>Search term</span>
<span>{valLabel}</span>
</div>
@ -83,7 +83,7 @@ export default class SearchTerms extends React.Component {
)
} else {
return (
<div className="text-center text-gray-700 text-sm mt-20">
<div className="text-center text-gray-700 dark:text-gray-300 text-sm mt-20">
<RocketIcon />
<div>Could not find any search terms for this period</div>
<div>Google Search Console data is sampled and delayed by 24-36h</div>
@ -97,7 +97,7 @@ export default class SearchTerms extends React.Component {
if (this.state.searchTerms) {
return (
<React.Fragment>
<h3 className="font-bold">Search Terms</h3>
<h3 className="font-bold dark:text-gray-100">Search Terms</h3>
{ this.renderList() }
<MoreLink site={this.props.site} list={this.state.searchTerms} endpoint="referrers/Google" />
</React.Fragment>
@ -107,7 +107,7 @@ export default class SearchTerms extends React.Component {
render() {
return (
<div className="stats-item relative bg-white shadow-xl rounded p-4" style={{height: '436px'}}>
<div className="stats-item relative bg-white dark:bg-gray-825 shadow-xl rounded p-4" style={{height: '436px'}}>
{ this.state.loading && <div className="loading mt-44 mx-auto"><div></div></div> }
<FadeIn show={!this.state.loading}>
{ this.renderContent() }

View File

@ -42,15 +42,15 @@ class AllSources extends React.Component {
return (
<div className="flex items-center justify-between my-1 text-sm" key={referrer.name}>
<div className="w-full h-8" style={{maxWidth: 'calc(100% - 4rem)'}}>
<Bar count={referrer.count} all={this.state.referrers} bg="bg-blue-50" />
<span className="flex px-2" style={{marginTop: '-26px'}} >
<Bar count={referrer.count} all={this.state.referrers} bg="bg-blue-50 dark:bg-gray-500 dark:bg-opacity-15" />
<span className="flex px-2 dark:text-gray-300" style={{marginTop: '-26px'}} >
<Link className="block truncate hover:underline" to={{search: query.toString()}}>
<img src={`https://icons.duckduckgo.com/ip3/${referrer.url}.ico`} referrerPolicy="no-referrer" className="inline h-4 w-4 mr-2 align-middle -mt-px" />
{ referrer.name }
</Link>
</span>
</div>
<span className="font-medium">{numberFormatter(referrer.count)}</span>
<span className="font-medium dark:text-gray-200">{numberFormatter(referrer.count)}</span>
</div>
)
}
@ -83,7 +83,7 @@ class AllSources extends React.Component {
return (
<React.Fragment>
<div className="w-full flex justify-between">
<h3 className="font-bold">Top sources</h3>
<h3 className="font-bold dark:text-gray-100">Top sources</h3>
{ this.props.renderTabs() }
</div>
{ this.renderList() }
@ -95,7 +95,7 @@ class AllSources extends React.Component {
render() {
return (
<div className="stats-item relative bg-white shadow-xl rounded p-4" style={{height: '436px'}}>
<div className="stats-item relative bg-white dark:bg-gray-825 shadow-xl rounded p-4" style={{height: '436px'}}>
{ this.state.loading && <div className="loading mt-44 mx-auto"><div></div></div> }
<FadeIn show={!this.state.loading}>
{ this.renderContent() }
@ -146,14 +146,14 @@ class UTMSources extends React.Component {
return (
<div className="flex items-center justify-between my-1 text-sm" key={referrer.name}>
<div className="w-full h-8" style={{maxWidth: 'calc(100% - 4rem)'}}>
<Bar count={referrer.count} all={this.state.referrers} bg="bg-blue-50" />
<span className="flex px-2" style={{marginTop: '-26px'}} >
<Bar count={referrer.count} all={this.state.referrers} bg="bg-blue-50 dark:bg-gray-500 dark:bg-opacity-15" />
<span className="flex px-2 dark:text-gray-300" style={{marginTop: '-26px'}} >
<Link className="block truncate hover:underline" to={{search: query.toString()}}>
{ referrer.name }
</Link>
</span>
</div>
<span className="font-medium">{numberFormatter(referrer.count)}</span>
<span className="font-medium dark:text-gray-200">{numberFormatter(referrer.count)}</span>
</div>
)
}
@ -166,7 +166,7 @@ class UTMSources extends React.Component {
if (this.state.referrers.length > 0) {
return (
<React.Fragment>
<div className="flex items-center mt-3 mb-2 justify-between text-gray-500 text-xs font-bold tracking-wide">
<div className="flex items-center mt-3 mb-2 justify-between text-gray-500 dark:text-gray-400 text-xs font-bold tracking-wide">
<span>{UTM_TAGS[this.props.tab].label}</span>
<span>{this.label()}</span>
</div>
@ -177,7 +177,7 @@ class UTMSources extends React.Component {
</React.Fragment>
)
} else {
return <div className="text-center mt-44 font-medium text-gray-500">No data yet</div>
return <div className="text-center mt-44 font-medium text-gray-500 dark:text-gray-400">No data yet</div>
}
}
@ -186,7 +186,7 @@ class UTMSources extends React.Component {
return (
<React.Fragment>
<div className="w-full flex justify-between">
<h3 className="font-bold">Top sources</h3>
<h3 className="font-bold dark:text-gray-100">Top sources</h3>
{ this.props.renderTabs() }
</div>
{ this.renderList() }
@ -198,7 +198,7 @@ class UTMSources extends React.Component {
render() {
return (
<div className="stats-item relative bg-white shadow-xl rounded p-4" style={{height: '436px'}}>
<div className="stats-item relative bg-white dark:bg-gray-825 shadow-xl rounded p-4" style={{height: '436px'}}>
{ this.state.loading && <div className="loading mt-44 mx-auto"><div></div></div> }
<FadeIn show={!this.state.loading}>
{ this.renderContent() }
@ -226,10 +226,10 @@ export default class SourceList extends React.Component {
}
renderTabs() {
const activeClass = 'inline-block h-5 text-indigo-700 font-bold border-b-2 border-indigo-700'
const defaultClass = 'hover:text-indigo-700 cursor-pointer'
const activeClass = 'inline-block h-5 text-indigo-700 dark:text-indigo-500 font-bold border-b-2 border-indigo-700 dark:border-indigo-500'
const defaultClass = 'hover:text-indigo-600 cursor-pointer'
return (
<ul className="flex font-medium text-xs text-gray-500 space-x-2">
<ul className="flex font-medium text-xs text-gray-500 dark:text-gray-400 space-x-2">
<li className={this.state.tab === 'all' ? activeClass : defaultClass} onClick={this.setTab('all')}>All</li>
<li className={this.state.tab === 'utm_medium' ? activeClass : defaultClass} onClick={this.setTab('utm_medium')}>Medium</li>
<li className={this.state.tab === 'utm_source' ? activeClass : defaultClass} onClick={this.setTab('utm_source')}>Source</li>

View File

@ -4,6 +4,7 @@ import Chart from 'chart.js'
import { eventName, navigateToQuery } from '../query'
import numberFormatter, {durationFormatter} from '../number-formatter'
import * as api from '../api'
import {ThemeContext} from '../theme-context'
function buildDataSet(plot, present_index, ctx, label) {
var gradient = ctx.createLinearGradient(0, 0, 0, 300);
@ -92,13 +93,18 @@ function dateFormatter(interval, longForm) {
}
class LineGraph extends React.Component {
componentDidMount() {
constructor(props) {
super(props);
this.regenerateChart = this.regenerateChart.bind(this);
}
regenerateChart() {
const {graphData} = this.props
this.ctx = document.getElementById("main-graph-canvas").getContext('2d');
const label = this.props.query.filters.goal ? 'Converted visitors' : graphData.interval === 'minute' ? 'Pageviews' : 'Visitors'
const dataSet = buildDataSet(graphData.plot, graphData.present_index, this.ctx, label)
this.chart = new Chart(this.ctx, {
return new Chart(this.ctx, {
type: 'line',
data: {
labels: graphData.labels,
@ -157,6 +163,7 @@ class LineGraph extends React.Component {
beginAtZero: true,
autoSkip: true,
maxTicksLimit: 8,
fontColor: this.props.darkTheme ? 'rgb(243, 244, 246)' : undefined
},
gridLines: {
zeroLineColor: 'transparent',
@ -171,6 +178,7 @@ class LineGraph extends React.Component {
autoSkip: true,
maxTicksLimit: 8,
callback: dateFormatter(graphData.interval),
fontColor: this.props.darkTheme ? 'rgb(243, 244, 246)' : undefined
}
}]
}
@ -178,6 +186,10 @@ class LineGraph extends React.Component {
});
}
componentDidMount() {
this.chart = this.regenerateChart();
}
componentDidUpdate(prevProps) {
if (this.props.graphData !== prevProps.graphData) {
const label = this.props.query.filters.goal ? 'Converted visitors' : this.props.graphData.interval === 'minute' ? 'Pageviews' : 'Visitors'
@ -189,6 +201,11 @@ class LineGraph extends React.Component {
this.chart.update()
}
if (prevProps.darkTheme !== this.props.darkTheme) {
this.chart = this.regenerateChart();
this.chart.update();
}
}
onClick(e) {
@ -220,12 +237,12 @@ class LineGraph extends React.Component {
if (comparison > 0) {
const color = name === 'Bounce rate' ? 'text-red-400' : 'text-green-500'
return <span className="text-xs"><span className={color + ' font-bold'}>&uarr;</span> {formattedComparison}%</span>
return <span className="text-xs dark:text-gray-100"><span className={color + ' font-bold'}>&uarr;</span> {formattedComparison}%</span>
} else if (comparison < 0) {
const color = name === 'Bounce rate' ? 'text-green-500' : 'text-red-400'
return <span className="text-xs"><span className={color + ' font-bold'}>&darr;</span> {formattedComparison}%</span>
return <span className="text-xs dark:text-gray-100"><span className={color + ' font-bold'}>&darr;</span> {formattedComparison}%</span>
} else if (comparison === 0) {
return <span className="text-xs text-gray-700">&#12336; N/A</span>
return <span className="text-xs text-gray-700 dark:text-gray-300">&#12336; N/A</span>
}
}
@ -247,9 +264,9 @@ class LineGraph extends React.Component {
return (
<div className={`px-8 w-1/2 my-4 lg:w-auto ${border}`} key={stat.name}>
<div className="text-gray-500 text-xs font-bold tracking-wide uppercase">{stat.name}</div>
<div className="text-gray-500 dark:text-gray-400 text-xs font-bold tracking-wide uppercase">{stat.name}</div>
<div className="my-1 flex justify-between items-center">
<b className="text-2xl mr-4">{ this.renderTopStatNumber(stat) }</b>
<b className="text-2xl mr-4 dark:text-gray-100">{ this.renderTopStatNumber(stat) }</b>
{this.renderComparison(stat.name, stat.change)}
</div>
</div>
@ -268,7 +285,7 @@ class LineGraph extends React.Component {
return (
<a href={endpoint} download>
<svg className="feather w-4 h-5 absolute text-gray-700" style={{right: '2rem', top: '-2rem'}} xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"></path><polyline points="7 10 12 15 17 10"></polyline><line x1="12" y1="15" x2="12" y2="3"></line></svg>
<svg className="feather w-4 h-5 absolute text-gray-700 dark:text-gray-300" style={{right: '2rem', top: '-2rem'}} xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"></path><polyline points="7 10 12 15 17 10"></polyline><line x1="12" y1="15" x2="12" y2="3"></line></svg>
</a>
)
}
@ -321,14 +338,18 @@ export default class VisitorGraph extends React.Component {
renderInner() {
if (this.state.graphData) {
return (
<LineGraph graphData={this.state.graphData} site={this.props.site} query={this.props.query} />
<ThemeContext.Consumer>
{theme => (
<LineGraph graphData={this.state.graphData} site={this.props.site} query={this.props.query} darkTheme={theme}/>
)}
</ThemeContext.Consumer>
)
}
}
render() {
return (
<div className="w-full relative bg-white shadow-xl rounded mt-6 main-graph">
<div className="w-full relative bg-white dark:bg-gray-825 shadow-xl rounded mt-6 main-graph">
{ this.state.loading && <div className="loading pt-24 sm:pt-32 md:pt-48 mx-auto"><div></div></div> }
{ this.renderInner() }
</div>

View File

@ -0,0 +1,16 @@
import React from 'react';
import { ThemeContext } from './theme-context'
export const withThemeConsumer = (WrappedComponent) => {
return class extends React.Component {
render() {
return (
<ThemeContext.Consumer>
{theme => (
<WrappedComponent darkTheme={theme} {...this.props} />
)}
</ThemeContext.Consumer>
);
}
}
}

View File

@ -0,0 +1,5 @@
import React from 'react';
export const ThemeContext = React.createContext({
dark: false
});

View File

@ -0,0 +1,37 @@
import React from 'react';
import { ThemeContext } from './theme-context'
export const withThemeProvider = (WrappedComponent) => {
return class extends React.Component {
constructor(props) {
super(props)
this.state = {
dark: document.querySelector('html').classList.contains('dark') || false
};
this.mutationObserver = new MutationObserver((mutationsList, observer) => {
mutationsList.forEach(mutation => {
if (mutation.attributeName === 'class') {
this.setState({ dark: mutation.target.classList.contains('dark') });
}
});
});
}
componentDidMount() {
this.mutationObserver.observe(document.querySelector('html'), { attributes: true });
}
componentWillUnmount() {
this.mutationObserver.disconnect();
}
render() {
return (
<ThemeContext.Provider value={this.state.dark}>
<WrappedComponent {...this.props}/>
</ThemeContext.Provider>
);
}
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

View File

@ -0,0 +1,37 @@
var pref = document.currentScript.dataset.pref;
function reapplyTheme() {
var userPref = pref || "system";
var mediaPref = window.matchMedia('(prefers-color-scheme: dark)').matches;
var htmlRef = document.querySelector('html');
var hcaptchaRefs = document.getElementsByClassName('h-captcha');
htmlRef.classList.remove('dark');
for (let i = 0; i < hcaptchaRefs.length; i++) {
hcaptchaRefs[i].dataset.theme = "light";
}
switch (userPref) {
case "dark":
htmlRef.classList.add('dark');
for (let i = 0; i < hcaptchaRefs.length; i++) {
hcaptchaRefs[i].dataset.theme = "dark";
}
break;
case "system":
if (mediaPref) {
htmlRef.classList.add('dark');
for (let i = 0; i < hcaptchaRefs.length; i++) {
hcaptchaRefs[i].dataset.theme = "dark";
}
}
break;
}
}
reapplyTheme();
window.matchMedia('(prefers-color-scheme: dark)').addListener(reapplyTheme);
window.onload = function() {
reapplyTheme();
};

View File

@ -5,7 +5,7 @@ module.exports = {
'./js/**/*.js',
'../lib/plausible_web/templates/**/*.html.eex',
],
darkMode: false,
darkMode: 'class',
theme: {
container: {
center: true,
@ -14,18 +14,29 @@ module.exports = {
extend: {
colors: {
orange: colors.orange,
'gray-850': 'rgb(26, 32, 44)',
'gray-825': 'rgb(37, 47, 63)'
},
spacing: {
'44': '11rem'
},
width: {
'31percent': '31%',
},
opacity: {
'15': '0.15',
}
},
},
variants: {
textColor: ['responsive', 'hover', 'focus', 'group-hover'],
display: ['responsive', 'hover', 'focus', 'group-hover']
display: ['responsive', 'hover', 'focus', 'group-hover'],
extend: {
textColor: ['dark'],
borderWidth: ['dark'],
backgroundOpacity: ['dark'],
display: ['dark'],
}
},
plugins: [
require('@tailwindcss/forms'),

View File

@ -17,6 +17,7 @@ defmodule Plausible.Auth.User do
field :name, :string
field :last_seen, :naive_datetime
field :trial_expiry_date, :date
field :theme, :string
field :email_verified, :boolean
has_many :site_memberships, Plausible.Site.Membership
@ -40,7 +41,7 @@ defmodule Plausible.Auth.User do
def changeset(user, attrs \\ %{}) do
user
|> cast(attrs, [:email, :name, :email_verified])
|> cast(attrs, [:email, :name, :email_verified, :theme])
|> validate_required([:email, :name, :email_verified])
|> unique_constraint(:email)
end

11
lib/plausible/themes.ex Normal file
View File

@ -0,0 +1,11 @@
defmodule Plausible.Themes do
@options [
[key: "Follow System Theme", value: "system"],
[key: "Light", value: "light"],
[key: "Dark", value: "dark"]
]
def options() do
@options
end
end

View File

@ -272,7 +272,8 @@ defmodule PlausibleWeb.AuthController do
render(conn, "user_settings.html",
changeset: changeset,
subscription: conn.assigns[:current_user].subscription
subscription: conn.assigns[:current_user].subscription,
theme: conn.assigns[:current_user].theme || "system"
)
end

View File

@ -6,30 +6,30 @@
<!-- Complete Step -->
<li class="flex items-start">
<span class="flex-shrink-0 relative h-5 w-5 flex items-center justify-center">
<svg class="h-full w-full text-indigo-600" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor">
<svg class="h-full w-full text-indigo-600 dark:text-indigo-500" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor">
<path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zm3.707-9.293a1 1 0 00-1.414-1.414L9 10.586 7.707 9.293a1 1 0 00-1.414 1.414l2 2a1 1 0 001.414 0l4-4z" clip-rule="evenodd" />
</svg>
</span>
<span class="ml-3 text-sm font-medium text-gray-500"><%= step %></span>
<span class="ml-3 text-sm font-medium text-gray-500 dark:text-gray-300"><%= step %></span>
</li>
<% end %>
<%= if index == @current_step do %>
<!-- Current Step -->
<li class="flex items-start">
<span class="flex-shrink-0 h-5 w-5 relative flex items-center justify-center">
<span class="absolute h-4 w-4 rounded-full bg-indigo-200"></span>
<span class="relative block w-2 h-2 bg-indigo-600 rounded-full"></span>
<span class="absolute h-4 w-4 rounded-full bg-indigo-200 dark:bg-indigo-100"></span>
<span class="relative block w-2 h-2 bg-indigo-600 dark:bg-indigo-500 rounded-full"></span>
</span>
<span class="ml-3 text-sm font-medium text-indigo-600"><%= step %></span>
<span class="ml-3 text-sm font-medium text-indigo-600 dark:text-indigo-500"><%= step %></span>
</li>
<% end %>
<%= if index > @current_step do %>
<!-- Upcoming Step -->
<li class="flex items-start">
<div class="flex-shrink-0 h-5 w-5 relative flex items-center justify-center">
<div class="h-2 w-2 bg-gray-300 rounded-full"></div>
<div class="h-2 w-2 bg-gray-300 dark:bg-gray-700 rounded-full"></div>
</div>
<p class="ml-3 text-sm font-medium text-gray-500"><%= step %></p>
<p class="ml-3 text-sm font-medium text-gray-500 dark:text-gray-300"><%= step %></p>
</li>
<% end %>
<% end %>

View File

@ -1,32 +1,32 @@
<div class="w-full max-w-3xl mt-4 mx-auto flex">
<%= if @has_pin do %>
<%= form_for @conn, "/activate", [class: "w-full max-w-lg mx-auto bg-white shadow-md rounded px-8 py-6 mb-4 mt-8"], fn f -> %>
<h2 class="text-xl font-black">Activate your account</h2>
<%= form_for @conn, "/activate", [class: "w-full max-w-lg mx-auto bg-white dark:bg-gray-800 shadow-md rounded px-8 py-6 mb-4 mt-8"], fn f -> %>
<h2 class="text-xl font-black dark:text-gray-100">Activate your account</h2>
<div class="mt-2 text-sm text-gray-500 leading-tight">
<div class="mt-2 text-sm text-gray-500 dark:text-gray-200 leading-tight">
Please enter the 4-digit code we sent to <b><%= @conn.assigns[:current_user].email %></b>
</div>
<div class="mt-12 flex items-stretch flex-grow">
<div>
<%= text_input f, :code, class: "tracking-widest font-medium shadow-sm focus:ring-indigo-500 focus:border-indigo-500 block w-36 px-8 border-gray-300 rounded-l-md", oninput: "this.value=this.value.replace(/[^0-9]/g, ''); if (this.value.length >= 4) document.getElementById('submit').focus()", onclick: "this.select();", maxlength: "4", placeholder: "••••", style: "letter-spacing: 10px;" %>
<%= text_input f, :code, class: "tracking-widest font-medium shadow-sm focus:ring-indigo-500 focus:border-indigo-500 block w-36 px-8 border-gray-300 dark:border-gray-500 rounded-l-md dark:text-gray-200 dark:bg-gray-900", oninput: "this.value=this.value.replace(/[^0-9]/g, ''); if (this.value.length >= 4) document.getElementById('submit').focus()", onclick: "this.select();", maxlength: "4", placeholder: "••••", style: "letter-spacing: 10px;" %>
</div>
<button id="submit" class="button rounded-l-none">Activate &rarr;</button>
</div>
<%= error_tag(assigns, :error) %>
<div class="mt-16 text-sm">
<div class="mt-16 text-sm dark:text-gray-100">
Didn't receive an email?
</div>
<div class="mt-2 text-sm text-gray-500 leading-tight">
Please check your spam folder and contact <a class="underline text-gray-800" href="mailto:support@plausible.io">support@plausible.io</a> if the problem persists
Please check your spam folder and contact <a class="underline text-indigo-500" href="mailto:support@plausible.io">support@plausible.io</a> if the problem persists
</div>
<% end %>
<% else %>
<div class="w-full max-w-lg mx-auto bg-white shadow-md rounded px-8 py-6 mb-4 mt-8">
<h2 class="text-xl font-black">Activate your account</h2>
<div class="w-full max-w-lg mx-auto bg-white dark:bg-gray-800 shadow-md rounded px-8 py-6 mb-4 mt-8">
<h2 class="text-xl font-black dark:text-gray-100">Activate your account</h2>
<div class="mt-2 text-sm text-gray-500 leading-tight">
<div class="mt-2 text-sm text-gray-500 dark:text-gray-200 leading-tight">
A 4-digit activation code will be sent to <b><%= @conn.assigns[:current_user].email %></b>
</div>

View File

@ -1,5 +1,5 @@
<%= form_for @conn, "/login", [class: "w-full max-w-md mx-auto bg-white shadow-md rounded px-8 py-6 mt-8"], fn f -> %>
<h1 class="text-xl font-black"><%= get_flash(@conn, :login_title) || "Enter your email and password" %></h1>
<%= form_for @conn, "/login", [class: "w-full max-w-md mx-auto bg-white dark:bg-gray-800 shadow-md rounded px-8 py-6 mt-8"], fn f -> %>
<h1 class="text-xl font-black dark:text-gray-100"><%= get_flash(@conn, :login_title) || "Enter your email and password" %></h1>
<%= if get_flash(@conn, :login_instructions) do %>
<p class="text-gray-500 text-sm mt-1 mb-2"><%= get_flash(@conn, :login_instructions) %></p>
<% end %>
@ -7,18 +7,18 @@
<div class="text-red-500 text-xs italic mt-4"><%= @conn.assigns[:error] %></div>
<% end %>
<div class="my-4 mt-8">
<%= label f, :email, class: "block text-gray-700 text-sm font-bold mb-2" %>
<%= email_input f, :email, class: "bg-gray-100 appearance-none border border-transparent rounded w-full p-2 text-gray-700 leading-normal appearance-none focus:outline-none focus:bg-white focus:border-gray-300", placeholder: "user@example.com" %>
<%= label f, :email, class: "block text-gray-700 dark:text-gray-300 text-sm font-bold mb-2" %>
<%= email_input f, :email, class: "bg-gray-100 dark:bg-gray-900 outline-none appearance-none border border-transparent rounded w-full p-2 text-gray-700 dark:text-gray-300 leading-normal appearance-none focus:outline-none focus:bg-white dark:focus:bg-gray-800 focus:border-gray-300 dark:focus:border-gray-500", placeholder: "user@example.com" %>
</div>
<div class="my-4">
<%= label f, :password, class: "block text-gray-700 text-sm font-bold mb-2" %>
<%= password_input f, :password, class: "transition bg-gray-100 appearance-none border border-transparent rounded w-full p-2 text-gray-700 leading-normal appearance-none focus:outline-none focus:bg-white focus:border-gray-300" %>
<p class="text-gray-500 text-xs my-2">Forgot password? <a href="/password/request-reset" class="underline text-gray-800">Click here</a> to reset it.</p>
<%= label f, :password, class: "block text-gray-700 dark:text-gray-300 text-sm font-bold mb-2" %>
<%= password_input f, :password, class: "transition bg-gray-100 dark:bg-gray-900 outline-none appearance-none border border-transparent rounded w-full p-2 text-gray-700 dark:text-gray-300 leading-normal appearance-none focus:outline-none focus:bg-white dark:focus:bg-gray-800 focus:border-gray-300 dark:focus:border-gray-500" %>
<p class="text-gray-500 text-xs my-2">Forgot password? <a href="/password/request-reset" class="underline text-gray-800 dark:text-gray-50">Click here</a> to reset it.</p>
</div>
<%= submit "Login →", class: "button mt-4 w-full" %>
<%= if !Keyword.fetch!(Application.get_env(:plausible, :selfhost),:disable_registration) do %>
<p class="text-center text-gray-500 text-xs mt-4">
Don't have an account? <%= link("Register", to: "/register", class: "underline text-gray-800") %> instead.
Don't have an account? <%= link("Register", to: "/register", class: "text-gray-800 dark:text-gray-50 underline") %> instead.
</p>
<% end %>
<% end %>

View File

@ -1,14 +1,14 @@
<%= form_for @conn, "/password", [class: "bg-white max-w-md w-full mx-auto shadow-md rounded px-8 py-6 mt-8"], fn f -> %>
<h2 class="text-xl font-black">Set your password</h2>
<%= form_for @conn, "/password", [class: "bg-white dark:bg-gray-800 max-w-md w-full mx-auto shadow-md rounded px-8 py-6 mt-8"], fn f -> %>
<h2 class="text-xl font-black dark:text-gray-100">Set your password</h2>
<div class="my-4">
<p class="text-gray-600 text-sm mt-1 mb-2">Min 6 characters</p>
<%= password_input f, :password, class: "transition bg-gray-100 appearance-none border border-transparent rounded w-full p-2 text-gray-700 leading-normal appearance-none focus:outline-none focus:bg-white focus:border-gray-300" %>
<p class="text-gray-600 dark:text-gray-400 text-sm mt-1 mb-2">Min 6 characters</p>
<%= password_input f, :password, class: "transition bg-gray-100 dark:bg-gray-900 outline-none appearance-none border border-transparent rounded w-full p-2 text-gray-700 dark:text-gray-300 leading-normal appearance-none focus:outline-none focus:bg-white dark:focus:bg-gray-800 focus:border-gray-300 dark:focus:border-gray-500" %>
<%= if @conn.assigns[:changeset] do %>
<%= error_tag @changeset, :password %>
<% end %>
</div>
<%= submit "Set password →", class: "button mt-4 w-full" %>
<p class="text-center text-gray-600 text-xs mt-4">
Don't have an account? <%= link("Register", to: "/register") %> instead.
<p class="text-center text-gray-500 text-xs mt-4">
Don't have an account? <%= link("Register", to: "/register", class: "underline text-gray-800 dark:text-gray-200") %> instead.
</p>
<% end %>

View File

@ -1,15 +1,15 @@
<%= form_for @conn, "/password/reset", [class: "bg-white max-w-md w-full mx-auto shadow-md rounded px-8 py-6 mt-8"], fn f -> %>
<h2 class="text-xl font-black">Reset your password</h2>
<%= form_for @conn, "/password/reset", [class: "bg-white dark:bg-gray-800 max-w-md w-full mx-auto shadow-md rounded px-8 py-6 mt-8"], fn f -> %>
<h2 class="text-xl font-black dark:text-gray-100">Reset your password</h2>
<div class="my-4">
<p class="text-gray-600 text-sm mt-1 mb-2">Min 6 characters</p>
<%= password_input f, :password, class: "transition bg-gray-100 appearance-none border border-transparent rounded w-full p-2 text-gray-700 leading-normal appearance-none focus:outline-none focus:bg-white focus:border-gray-300" %>
<p class="text-gray-600 dark:text-gray-400 text-sm mt-1 mb-2">Min 6 characters</p>
<%= password_input f, :password, class: "transition bg-gray-100 dark:bg-gray-900 outline-none appearance-none border border-transparent rounded w-full p-2 text-gray-700 dark:text-gray-300 leading-normal appearance-none focus:outline-none focus:bg-white dark:focus:bg-gray-800 focus:border-gray-300 dark:focus:border-gray-500" %>
<%= if @conn.assigns[:changeset] do %>
<%= error_tag @changeset, :password %>
<% end %>
</div>
<%= hidden_input f, :token, value: @token %>
<%= submit "Set password →", class: "button mt-4 w-full" %>
<p class="text-center text-gray-600 text-xs mt-4">
Don't have an account? <%= link("Register", to: "/register") %> instead.
<p class="text-center text-gray-500 text-xs mt-4">
Don't have an account? <%= link("Register", to: "/register", class: "underline text-gray-800 dark:text-gray-200") %> instead.
</p>
<% end %>

View File

@ -1,8 +1,8 @@
<%= form_for @conn, "/password/request-reset", [class: "max-w-md w-full mx-auto bg-white shadow-md rounded px-8 py-6 mt-8"], fn f -> %>
<h1 class="text-xl font-black">Reset your password</h1>
<div class="mt-4">Enter your email so we can send a password reset link</div>
<%= form_for @conn, "/password/request-reset", [class: "max-w-md w-full mx-auto bg-white dark:bg-gray-800 shadow-md rounded px-8 py-6 mt-8"], fn f -> %>
<h1 class="text-xl font-black dark:text-gray-100">Reset your password</h1>
<div class="mt-4 dark:text-gray-100">Enter your email so we can send a password reset link</div>
<div class="my-4 mt-8">
<%= email_input f, :email, class: "transition bg-gray-100 appearance-none border border-transparent rounded w-full p-2 text-gray-700 leading-normal appearance-none focus:outline-none focus:bg-white focus:border-gray-300", placeholder: "user@example.com" %>
<%= email_input f, :email, class: "transition bg-gray-100 dark:bg-gray-900 outline-none appearance-none border border-transparent rounded w-full p-2 text-gray-700 dark:text-gray-300 leading-normal focus:outline-none focus:bg-white dark:focus:bg-gray-800 focus:border-gray-300 dark:focus:border-gray-500", placeholder: "user@example.com" %>
</div>
<%= if @conn.assigns[:error] do %>
<div class="text-red-500 text-xs italic my-2"><%= @conn.assigns[:error] %></div>

View File

@ -1,12 +1,12 @@
<div class="bg-white max-w-md w-full mx-auto shadow-md rounded px-8 pt-6 pb-8 mb-4 mt-8">
<h2 class="text-xl font-black">Success!</h2>
<div class="my-4 leading-tight">
<div class="bg-white dark:bg-gray-800 max-w-md w-full mx-auto shadow-md rounded px-8 pt-6 pb-8 mb-4 mt-8">
<h2 class="text-xl font-black dark:text-gray-100">Success!</h2>
<div class="my-4 leading-tight dark:text-gray-100">
We have sent an email with password reset instructions to <b><%= @email %></b> if it exists in our database.
</div>
<div class="mt-8 text-sm">
<div class="mt-8 text-sm dark:text-gray-100">
Didn't receive an email?
</div>
<div class="mt-2 text-sm text-gray-600 leading-tight">
Please check your spam folder and contact <a href="mailto:support@plausible.io">support@plausible.io</a> if the problem persists
<div class="mt-2 text-sm text-gray-600 dark:text-gray-400 leading-tight">
Please check your spam folder and contact <a href="mailto:support@plausible.io" class="text-indigo-500">support@plausible.io</a> if the problem persists
</div>
</div>

View File

@ -1,51 +1,51 @@
<div class="mx-auto mt-6 text-center">
<div class="mx-auto mt-6 text-center dark:text-gray-300">
<h1 class="text-3xl font-black">Register your 30-day unlimited-use free trial</h1>
<div class="text-xl font-medium">Set up privacy-friendly analytics with just a few clicks</div>
</div>
<div class="w-full max-w-3xl mt-4 mx-auto flex">
<%= form_for @changeset, "/register", [class: "w-full max-w-md mx-auto bg-white shadow-md rounded px-8 py-6 mb-4 mt-8", id: "register-form"], fn f -> %>
<h2 class="text-xl font-black">Enter your details</h2>
<%= form_for @changeset, "/register", [class: "w-full max-w-md mx-auto bg-white dark:bg-gray-800 shadow-md rounded px-8 py-6 mb-4 mt-8", id: "register-form"], fn f -> %>
<h2 class="text-xl font-black dark:text-gray-100">Enter your details</h2>
<div class="my-4">
<%= label f, :name, "Full name", class: "block text-sm font-medium text-gray-700" %>
<%= label f, :name, "Full name", class: "block text-sm font-medium text-gray-700 dark:text-gray-300" %>
<div class="mt-1">
<%= text_input f, :name, class: "shadow-sm focus:ring-indigo-500 focus:border-indigo-500 block w-full sm:text-sm border-gray-300 rounded-md", placeholder: "Jane Doe" %>
<%= text_input f, :name, class: "dark:bg-gray-900 shadow-sm focus:ring-indigo-500 focus:border-indigo-500 block w-full sm:text-sm border-gray-300 dark:border-gray-500 rounded-md dark:text-gray-300", placeholder: "Jane Doe" %>
</div>
<%= error_tag f, :name %>
</div>
<div class="my-4">
<div class="flex justify-between">
<%= label f, :email, class: "block text-sm font-medium text-gray-700" %>
<%= label f, :email, class: "block text-sm font-medium text-gray-700 dark:text-gray-300" %>
<p class="text-xs text-gray-500 mt-1">No spam, guaranteed.</p>
</div>
<div class="mt-1">
<%= email_input f, :email, class: "shadow-sm focus:ring-indigo-500 focus:border-indigo-500 block w-full sm:text-sm border-gray-300 rounded-md", placeholder: "example@email.com" %>
<%= email_input f, :email, class: "dark:bg-gray-900 shadow-sm focus:ring-indigo-500 focus:border-indigo-500 block w-full sm:text-sm border-gray-300 dark:border-gray-500 rounded-md dark:text-gray-300", placeholder: "example@email.com" %>
</div>
<%= error_tag f, :email %>
</div>
<div class="my-4">
<div class="flex justify-between">
<%= label f, :password, class: "block text-sm font-medium text-gray-700" %>
<%= label f, :password, class: "block text-sm font-medium text-gray-700 dark:text-gray-300" %>
<p class="text-xs text-gray-500 mt-1">Min 6 characters</p>
</div>
<div class="mt-1">
<%= password_input f, :password, class: "shadow-sm focus:ring-indigo-500 focus:border-indigo-500 block w-full sm:text-sm border-gray-300 rounded-md" %>
<%= password_input f, :password, class: "dark:bg-gray-900 shadow-sm focus:ring-indigo-500 focus:border-indigo-500 block w-full sm:text-sm border-gray-300 dark:border-gray-500 rounded-md dark:text-gray-300" %>
</div>
<%= error_tag f, :password %>
</div>
<div class="my-4">
<%= label f, :password_confirmation, class: "block text-sm font-medium text-gray-700" %>
<%= label f, :password_confirmation, class: "block text-sm font-medium text-gray-700 dark:text-gray-300" %>
<div class="mt-1">
<%= password_input f, :password_confirmation, class: "shadow-sm focus:ring-indigo-500 focus:border-indigo-500 block w-full sm:text-sm border-gray-300 rounded-md" %>
<%= password_input f, :password_confirmation, class: "dark:bg-gray-900 shadow-sm focus:ring-indigo-500 focus:border-indigo-500 block w-full sm:text-sm border-gray-300 dark:border-gray-500 rounded-md dark:text-gray-300" %>
</div>
<%= error_tag f, :password_confirmation %>
</div>
<%= if PlausibleWeb.Captcha.enabled?() do %>
<div class="mt-4">
<div class="h-captcha" data-sitekey="<%= PlausibleWeb.Captcha.sitekey() %>"></div>
<div class="h-captcha"></div>
<%= if assigns[:captcha_error] do %>
<div class="text-red-500 text-xs italic mt-3"><%= @captcha_error %></div>
<% end %>
@ -55,8 +55,8 @@
<%= submit "Start my free trial →", class: "button mt-4 w-full" %>
<p class="text-center text-gray-600 text-xs mt-4">
Already have an account? <%= link("Log in", to: "/login", class: "underline text-gray-800") %> instead.
<p class="text-center text-gray-600 dark:text-gray-500 text-xs mt-4">
Already have an account? <%= link("Log in", to: "/login", class: "underline text-gray-800 dark:text-gray-50") %> instead.
</p>
<% end %>
<div class="pt-12 pl-8 hidden md:block">

View File

@ -1,20 +1,20 @@
<div class="mx-auto mt-6 text-center">
<div class="mx-auto mt-6 text-center dark:text-gray-300">
<h1 class="text-3xl font-black">Register your 30-day unlimited-use free trial</h1>
<div class="text-xl font-medium">Set up privacy-friendly analytics with just a few clicks</div>
</div>
<div class="w-full max-w-3xl mt-4 mx-auto flex">
<%= form_for @conn, "/claim-activation", [class: "w-full max-w-lg mx-auto bg-white shadow-md rounded px-8 py-6 mb-4 mt-8", id: "register-form"], fn f -> %>
<h2 class="text-xl font-black">Activate your account</h2>
<%= form_for @conn, "/claim-activation", [class: "w-full max-w-lg mx-auto bg-white dark:bg-gray-800 shadow-md rounded px-8 py-6 mb-4 mt-8", id: "register-form"], fn f -> %>
<h2 class="text-xl font-black dark:text-gray-100">Activate your account</h2>
<div class="mt-2 text-sm text-gray-500 leading-tight">
<div class="mt-2 text-sm text-gray-500 dark:text-gray-200 leading-tight">
Please enter the 4-digit code we sent to <b><%= @email %></b>
</div>
<div class="mt-12 flex items-stretch flex-grow">
<div>
<%= text_input f, :code, class: "tracking-widest font-medium shadow-sm focus:ring-indigo-500 focus:border-indigo-500 block w-36 px-8 border-gray-300 rounded-l-md", oninput: "this.value=this.value.replace(/[^0-9]/g, ''); if (this.value.length >= 4) document.getElementById('submit').focus()", onclick: "this.select();", maxlength: "4", placeholder: "••••", style: "letter-spacing: 10px;" %>
<%= text_input f, :code, class: "tracking-widest font-medium shadow-sm focus:ring-indigo-500 focus:border-indigo-500 block w-36 px-8 border-gray-300 dark:border-gray-500 rounded-l-md dark:text-gray-200 dark:bg-gray-900", oninput: "this.value=this.value.replace(/[^0-9]/g, ''); if (this.value.length >= 4) document.getElementById('submit').focus()", onclick: "this.select();", maxlength: "4", placeholder: "••••", style: "letter-spacing: 10px;" %>
<%= error_tag f, :name %>
</div>
<button id="submit" class="button rounded-l-none">Activate &rarr;</button>
@ -24,7 +24,7 @@
Didn't receive an email?
</div>
<div class="mt-2 text-sm text-gray-500 leading-tight">
Please check your spam folder and contact <a class="underline text-gray-800" href="mailto:support@plausible.io">support@plausible.io</a> if the problem persists
Please check your spam folder and contact <a class="underline text-indigo-500" href="mailto:support@plausible.io">support@plausible.io</a> if the problem persists
</div>
<% end %>

View File

@ -1,7 +1,7 @@
<%= if !Keyword.fetch!(Application.get_env(:plausible, :selfhost), :disable_subscription) do %>
<div class="max-w-2xl mx-auto bg-white shadow-md rounded rounded-t-none border-t-2 border-orange-200 px-8 pt-6 pb-8 mt-24">
<div class="max-w-2xl mx-auto bg-white dark:bg-gray-800 shadow-md rounded rounded-t-none border-t-2 border-orange-200 dark:border-orange-200 px-8 pt-6 pb-8 mt-24 ">
<div class="flex justify-between">
<h2 class="text-xl font-black">Subscription Plan</h2>
<h2 class="text-xl font-black dark:text-gray-100">Subscription Plan</h2>
<%= if @subscription do %>
<span class="inline-flex items-center px-2.5 py-0.5 rounded-md text-sm font-bold leading-5 <%= subscription_colors(@subscription.status) %>">
<%= present_subscription_status(@subscription.status) %>
@ -31,48 +31,48 @@
<% end %>
<div class="flex flex-col items-center sm:flex-row sm:items-start justify-between mt-8">
<div class="text-center bg-gray-100 py-4 px-2 rounded h-32 my-4" style="width: 11.75rem;">
<h4 class="font-black">Monthly quota</h4>
<div class="text-center bg-gray-100 dark:bg-gray-900 py-4 px-2 rounded h-32 my-4" style="width: 11.75rem;">
<h4 class="font-black dark:text-gray-100">Monthly quota</h4>
<%= if @subscription do %>
<div class="text-xl py-2 font-medium"><%= subscription_quota(@subscription) %> pageviews</div>
<div class="text-xl py-2 font-medium dark:text-gray-100"><%= subscription_quota(@subscription) %> pageviews</div>
<%= case @subscription.status do %>
<% "active" -> %>
<%= link("Change plan", to: "/billing/change-plan", class: "text-sm text-indigo-500 font-medium") %>
<% "past_due" -> %>
<span class="text-sm text-gray-600 font-medium" tooltip="Please update your billing details before changing plans">Change plan</span>
<span class="text-sm text-gray-600 dark:text-gray-400 font-medium" tooltip="Please update your billing details before changing plans">Change plan</span>
<% _ -> %>
<% end %>
<% else %>
<div class="text-xl py-2 font-medium">Free trial</div>
<div class="text-xl py-2 font-medium dark:text-gray-100">Free trial</div>
<%= link("Upgrade", to: "/billing/upgrade", class: "text-sm text-indigo-500 font-medium") %>
<% end %>
</div>
<div class="text-center bg-gray-100 py-4 px-2 rounded h-32 my-4" style="width: 11.75rem;">
<h4 class="font-black">Next bill amount</h4>
<div class="text-center bg-gray-100 dark:bg-gray-900 py-4 px-2 rounded h-32 my-4" style="width: 11.75rem;">
<h4 class="font-black dark:text-gray-100">Next bill amount</h4>
<%= if @subscription && @subscription.status in ["active", "past_due"] do %>
<div class="text-xl py-2 font-medium">$<%= @subscription.next_bill_amount %></div>
<div class="text-xl py-2 font-medium dark:text-gray-100">$<%= @subscription.next_bill_amount %></div>
<%= if @subscription.update_url do %>
<%= link("Update billing info", to: @subscription.update_url, class: "text-sm text-indigo-500 font-medium") %>
<% end %>
<% else %>
<div class="text-xl py-2 font-medium">---</div>
<div class="text-xl py-2 font-medium dark:text-gray-100">---</div>
<% end %>
</div>
<div class="text-center bg-gray-100 py-4 px-2 rounded h-32 my-4" style="width: 11.75rem;">
<h4 class="font-black">Next bill date</h4>
<div class="text-center bg-gray-100 dark:bg-gray-900 py-4 px-2 rounded h-32 my-4" style="width: 11.75rem;">
<h4 class="font-black dark:text-gray-100">Next bill date</h4>
<%= if @subscription && @subscription.next_bill_date && @subscription.status in ["active", "past_due"] do %>
<div class="text-xl py-2 font-medium"><%= Timex.format!(@subscription.next_bill_date, "{Mshort} {D}, {YYYY}") %></div>
<div class="text-sm text-gray-600 font-medium">(<%= subscription_interval(@subscription) %> billing)</div>
<div class="text-xl py-2 font-medium dark:text-gray-100"><%= Timex.format!(@subscription.next_bill_date, "{Mshort} {D}, {YYYY}") %></div>
<div class="text-sm text-gray-600 dark:text-gray-400 font-medium">(<%= subscription_interval(@subscription) %> billing)</div>
<% else %>
<div class="text-xl py-2 font-medium">---</div>
<div class="text-xl py-2 font-medium dark:text-gray-100">---</div>
<% end %>
</div>
</div>
<h3 class="text-xl font-bold mt-8">Your usage</h3>
<h3 class="text-xl font-bold mt-8 dark:text-gray-100">Your usage</h3>
<div class="py-2">
<div class="py-2 dark:text-gray-100">
<b><%= delimit_integer(Plausible.Billing.usage(@conn.assigns[:current_user])) %></b>
pageviews in the last 30 days
</div>
@ -80,7 +80,7 @@
<%= cond do %>
<% @subscription && @subscription.status in ["active", "past_due", "paused"] && @subscription.cancel_url -> %>
<div class="mt-8">
<%= link("Cancel my subscription", to: @subscription.cancel_url, class: "inline-block mt-4 px-4 py-2 border border-gray-300 text-sm leading-5 font-medium rounded-md text-red-700 bg-white hover:text-red-500 focus:outline-none focus:border-blue-300 focus:ring active:text-red-800 active:bg-gray-50 transition ease-in-out duration-150") %>
<%= link("Cancel my subscription", to: @subscription.cancel_url, class: "inline-block mt-4 px-4 py-2 border border-gray-300 dark:border-gray-500 text-sm leading-5 font-medium rounded-md text-red-700 bg-white dark:bg-gray-800 hover:text-red-500 focus:outline-none focus:border-blue-300 focus:ring active:text-red-800 active:bg-gray-50 transition ease-in-out duration-150") %>
</div>
<% true -> %>
<div class="mt-8">
@ -90,23 +90,37 @@
</div>
<% end %>
<div class="max-w-2xl mx-auto bg-white shadow-md rounded rounded-t-none border-t-2 border-indigo-100 px-8 pt-6 pb-8 mt-16">
<h2 class="text-xl font-black">Account settings</h2>
<div class="max-w-2xl mx-auto bg-white dark:bg-gray-800 shadow-md rounded rounded-t-none border-t-2 border-green-500 px-8 pt-6 pb-8 mt-16">
<h2 class="text-xl font-black dark:text-gray-100">Dashboard Appearance</h2>
<div class="my-4 border-b border-gray-300"></div>
<div class="my-4 border-b border-gray-300 dark:border-gray-500"></div>
<%= form_for @changeset, "/settings", [class: "max-w-sm"], fn f -> %>
<div class="col-span-4 sm:col-span-2">
<%= label f, :theme, "Theme Selection", class: "block text-sm font-medium leading-5 text-gray-700 dark:text-gray-300" %>
<%= select f, :theme, Plausible.Themes.options(), class: "dark:bg-gray-900 mt-1 block w-full pl-3 pr-10 py-2 text-base border-gray-300 dark:border-gray-500 focus:outline-none focus:ring-indigo-500 focus:border-indigo-500 sm:text-sm rounded-md dark:text-gray-100 cursor-pointer" %>
</div>
<%= submit "Save", class: "button mt-4" %>
<% end %>
</div>
<div class="max-w-2xl mx-auto bg-white dark:bg-gray-800 shadow-md rounded rounded-t-none border-t-2 border-indigo-100 dark:border-indigo-500 px-8 pt-6 pb-8 mt-16">
<h2 class="text-xl font-black dark:text-gray-100">Account settings</h2>
<div class="my-4 border-b border-gray-300 dark:border-gray-500"></div>
<%= form_for @changeset, "/settings", [class: "max-w-sm"], fn f -> %>
<div class="my-4">
<%= label f, :name, class: "block text-sm font-medium text-gray-700" %>
<%= label f, :name, class: "block text-sm font-medium text-gray-700 dark:text-gray-300" %>
<div class="mt-1">
<%= text_input f, :name, class: "shadow-sm focus:ring-indigo-500 focus:border-indigo-500 block w-full sm:text-sm border-gray-300 rounded-md" %>
<%= text_input f, :name, class: "shadow-sm dark:bg-gray-900 dark:text-gray-300 focus:ring-indigo-500 focus:border-indigo-500 block w-full sm:text-sm border-gray-300 dark:border-gray-500 rounded-md dark:bg-gray-800" %>
<%= error_tag f, :name %>
</div>
</div>
<div class="my-4">
<%= label f, :email, class: "block text-sm font-medium text-gray-700" %>
<%= label f, :email, class: "block text-sm font-medium text-gray-700 dark:text-gray-300" %>
<div class="mt-1">
<%= email_input f, :email, class: "shadow-sm focus:ring-indigo-500 focus:border-indigo-500 block w-full sm:text-sm border-gray-300 rounded-md" %>
<%= email_input f, :email, class: "shadow-sm dark:bg-gray-900 dark:text-gray-300 focus:ring-indigo-500 focus:border-indigo-500 block w-full sm:text-sm border-gray-300 dark:border-gray-500 rounded-md" %>
<%= error_tag f, :email %>
</div>
</div>
@ -114,19 +128,19 @@
<% end %>
</div>
<div class="max-w-2xl mx-auto bg-white shadow-md rounded rounded-t-none border-t-2 border-red-600 px-8 pt-6 pb-8 mt-16 mb-24">
<div class="max-w-2xl mx-auto bg-white dark:bg-gray-800 shadow-md rounded rounded-t-none border-t-2 border-red-600 px-8 pt-6 pb-8 mt-16 mb-24">
<div class="flex items-center justify-between">
<h2 class="text-xl font-black">Delete account</h2>
<h2 class="text-xl font-black dark:text-gray-100">Delete account</h2>
</div>
<div class="my-4 border-b border-gray-300"></div>
<div class="my-4 border-b border-gray-300 dark:border-gray-500"></div>
<p>Deleting your account removes all sites and stats you've collected</p>
<p class="dark:text-gray-100">Deleting your account removes all sites and stats you've collected</p>
<%= if @subscription && @subscription.status == "active" do %>
<span class="button bg-gray-300 mt-6 hover:shadow-none">Delete my account</span>
<p class="text-gray-600 text-sm mt-2">Your account cannot be deleted because you have an active subscription. If you want to delete your account, please cancel your subscription first.</p>
<span class="button bg-gray-300 dark:bg-gray-800 mt-6 hover:shadow-none">Delete my account</span>
<p class="text-gray-600 dark:text-gray-400 text-sm mt-2">Your account cannot be deleted because you have an active subscription. If you want to delete your account, please cancel your subscription first.</p>
<% else %>
<%= link("Delete my account", to: "/me", class: "inline-block mt-4 px-4 py-2 border border-gray-300 text-sm leading-5 font-medium rounded-md text-red-700 bg-white hover:text-red-500 focus:outline-none focus:border-blue-300 focus:ring active:text-red-800 active:bg-gray-50 transition ease-in-out duration-150", method: "delete", data: [confirm: "Deleting your account cannot be reversed. Are you sure?"]) %>
<%= link("Delete my account", to: "/me", class: "inline-block mt-4 px-4 py-2 border border-gray-300 dark:border-gray-500 text-sm leading-5 font-medium rounded-md text-red-700 bg-white dark:bg-gray-800 hover:text-red-500 dark:hover:text-red-400 focus:outline-none focus:border-blue-300 focus:ring active:text-red-800 active:bg-gray-50 transition ease-in-out duration-150", method: "delete", data: [confirm: "Deleting your account cannot be reversed. Are you sure?"]) %>
<% end %>
</div>

View File

@ -3,16 +3,16 @@
</script>
<div class="mx-auto mt-6 text-center">
<h1 class="text-3xl font-black">Change subscription plan</h1>
<h1 class="text-3xl font-black dark:text-gray-100">Change subscription plan</h1>
</div>
<div class="w-full max-w-lg px-4 mt-4 mx-auto">
<div x-data="{volume: '10k', billingCycle: 'monthly'}" class="flex-1 bg-white shadow-md rounded px-8 py-4 mb-4 mt-8">
<div class="w-full pt-2 text-xl font-bold">
<div x-data="{volume: '10k', billingCycle: 'monthly'}" class="flex-1 bg-white dark:bg-gray-800 shadow-md rounded px-8 py-4 mb-4 mt-8">
<div class="w-full pt-2 text-xl font-bold dark:text-gray-100">
Select your new plan
</div>
<div class="w-full text-gray-500 text-sm">
<div class="w-full text-gray-500 dark:text-gray-200 text-sm">
Depending on which plan you choose, your card might be charged immediately and your
next payment date could change. You can preview these changes before committing.
</div>
@ -20,12 +20,12 @@
<div class="pt-8"></div>
<span class="relative z-0 inline-flex shadow-sm w-full">
<button type="button" @click="billingCycle = 'monthly'" :class="{'bg-indigo-600 text-white border-indigo-600': billingCycle === 'monthly', 'bg-white text-gray-700 hover:text-gray-500 border-gray-300': billingCycle === 'yearly'}" class="relative w-full text-center px-4 py-2 rounded-l-md border text-sm leading-5 font-medium focus:outline-none focus:border-blue-300 focus:ring transition ease-in-out duration-150">
<button type="button" @click="billingCycle = 'monthly'" :class="{'bg-indigo-600 text-white border-indigo-600': billingCycle === 'monthly', 'bg-white dark:bg-gray-800 text-gray-700 dark:text-gray-300 hover:text-gray-500 dark:hover:text-gray-200 border-gray-300 dark:border-gray-500': billingCycle === 'yearly'}" class="relative w-full text-center px-4 py-2 rounded-l-md border text-sm leading-5 font-medium focus:outline-none focus:border-blue-300 focus:ring transition ease-in-out duration-150">
Monthly billing
</button>
<button type="button" @click="billingCycle = 'yearly'" :class="{'bg-indigo-600 text-white border-indigo-600': billingCycle === 'yearly', 'bg-white text-gray-700 hover:text-gray-500 border-gray-300': billingCycle === 'monthly'}" class="-ml-px relative w-full text-center px-4 py-2 rounded-r-md border text-sm leading-5 font-medium focus:outline-none focus:border-blue-300 focus:ring transition ease-in-out duration-150">
<button type="button" @click="billingCycle = 'yearly'" :class="{'bg-indigo-600 text-white border-indigo-600': billingCycle === 'yearly', 'bg-white dark:bg-gray-800 text-gray-700 dark:text-gray-300 hover:text-gray-500 dark:hover:text-gray-200 border-gray-300 dark:border-gray-500': billingCycle === 'monthly'}" class="-ml-px relative w-full text-center px-4 py-2 rounded-r-md border text-sm leading-5 font-medium focus:outline-none focus:border-blue-300 focus:ring transition ease-in-out duration-150">
Yearly billing
<span class="inline-flex items-center px-2 py-0.5 rounded text-xs font-medium leading-4 bg-yellow-100 text-yellow-800">
<span class="inline-flex items-center px-2 py-0.5 rounded text-xs font-medium leading-4 bg-yellow-200 text-yellow-800 dark:text-yellow-900">
-33%
</span>
</button>
@ -35,64 +35,64 @@
<div class="flex flex-col">
<div class="-my-2 py-2 overflow-x-auto sm:-mx-6 sm:px-6 lg:-mx-8 lg:px-8">
<div class="align-middle inline-block min-w-full shadow overflow-hidden sm:rounded-lg border-b border-gray-200">
<div class="align-middle inline-block min-w-full shadow overflow-hidden sm:rounded-lg border-b border-gray-200 dark:border-t dark:border-l dark:border-r dark:shadow-none">
<table class="min-w-full">
<thead>
<tr>
<th class="px-6 py-3 border-b border-gray-200 bg-gray-100 text-left text-xs leading-4 font-medium text-gray-500 uppercase tracking-wider">
<th class="px-6 py-3 border-b border-gray-200 bg-gray-100 dark:bg-gray-900 text-left text-xs leading-4 font-medium text-gray-500 dark:text-gray-200 uppercase tracking-wider">
Monthly pageviews
</th>
<th class="px-6 py-3 border-b border-gray-200 bg-gray-100 text-left text-xs leading-4 font-medium text-gray-500 uppercase tracking-wider">
<th class="px-6 py-3 border-b border-gray-200 bg-gray-100 dark:bg-gray-900 text-left text-xs leading-4 font-medium text-gray-500 dark:text-gray-200 uppercase tracking-wider">
Price per month
</th>
</tr>
</thead>
<tbody class="bg-white">
<tr @click="volume = '10k'" :class="{'border-2 border-green-300 bg-green-50': volume === '10k', 'border-b border-gray-200 cursor-pointer': volume !== '10k'}">
<td class="px-6 py-4 text-sm leading-5" :class="{'font-bold': volume === '10k'}">10k</td>
<td class="px-6 py-4 text-sm leading-5" :class="{'font-bold': volume === '10k'}">
<tbody class="bg-white dark:bg-gray-800">
<tr @click="volume = '10k'" :class="{'border-2 border-green-300 bg-opacity-20 bg-green-300': volume === '10k', 'border-b border-gray-200 cursor-pointer': volume !== '10k'}">
<td class="px-6 py-4 text-sm leading-5 dark:text-gray-100" :class="{'font-bold': volume === '10k'}">10k</td>
<td class="px-6 py-4 text-sm leading-5 dark:text-gray-100" :class="{'font-bold': volume === '10k'}">
<span x-show="billingCycle === 'monthly'">$6</span>
<span x-show="billingCycle === 'yearly'">$4</span>
</td>
</tr>
<tr @click="volume = '100k'" :class="{'border-2 border-green-300 bg-green-50': volume === '100k', 'border-b border-gray-200 cursor-pointer': volume !== '100k'}">
<td class="px-6 py-4 text-sm leading-5" :class="{'font-bold': volume === '100k'}">100k</td>
<td class="px-6 py-4 text-sm leading-5" :class="{'font-bold': volume === '100k'}">
<tr @click="volume = '100k'" :class="{'border-2 border-green-300 bg-opacity-20 bg-green-300': volume === '100k', 'border-b border-gray-200 cursor-pointer': volume !== '100k'}">
<td class="px-6 py-4 text-sm leading-5 dark:text-gray-100" :class="{'font-bold': volume === '100k'}">100k</td>
<td class="px-6 py-4 text-sm leading-5 dark:text-gray-100" :class="{'font-bold': volume === '100k'}">
<span x-show="billingCycle === 'monthly'">$12</span>
<span x-show="billingCycle === 'yearly'">$8</span>
</td>
</tr>
<tr @click="volume = '200k'" :class="{'border-2 border-green-300 bg-green-50': volume === '200k', 'border-b border-gray-200 cursor-pointer': volume !== '200k'}">
<td class="px-6 py-4 text-sm leading-5" :class="{'font-bold': volume === '200k'}">200k</td>
<td class="px-6 py-4 text-sm leading-5" :class="{'font-bold': volume === '200k'}">
<tr @click="volume = '200k'" :class="{'border-2 border-green-300 bg-opacity-20 bg-green-300': volume === '200k', 'border-b border-gray-200 cursor-pointer': volume !== '200k'}">
<td class="px-6 py-4 text-sm leading-5 dark:text-gray-100" :class="{'font-bold': volume === '200k'}">200k</td>
<td class="px-6 py-4 text-sm leading-5 dark:text-gray-100" :class="{'font-bold': volume === '200k'}">
<span x-show="billingCycle === 'monthly'">$18</span>
<span x-show="billingCycle === 'yearly'">$12</span>
</td>
</tr>
<tr @click="volume = '500k'" :class="{'border-2 border-green-300 bg-green-50': volume === '500k', 'border-b border-gray-200 cursor-pointer': volume !== '500k'}">
<td class="px-6 py-4 text-sm leading-5" :class="{'font-bold': volume === '500k'}">500k</td>
<td class="px-6 py-4 text-sm leading-5" :class="{'font-bold': volume === '500k'}">
<tr @click="volume = '500k'" :class="{'border-2 border-green-300 bg-opacity-20 bg-green-300': volume === '500k', 'border-b border-gray-200 cursor-pointer': volume !== '500k'}">
<td class="px-6 py-4 text-sm leading-5 dark:text-gray-100" :class="{'font-bold': volume === '500k'}">500k</td>
<td class="px-6 py-4 text-sm leading-5 dark:text-gray-100" :class="{'font-bold': volume === '500k'}">
<span x-show="billingCycle === 'monthly'">$27</span>
<span x-show="billingCycle === 'yearly'">$18</span>
</td>
</tr>
<tr @click="volume = '1m'" :class="{'border-2 border-green-300 bg-green-50': volume === '1m', 'border-b border-gray-200 cursor-pointer': volume !== '1m'}">
<td class="px-6 py-4 text-sm leading-5" :class="{'font-bold': volume === '1m'}">1m</td>
<td class="px-6 py-4 text-sm leading-5" :class="{'font-bold': volume === '1m'}">
<tr @click="volume = '1m'" :class="{'border-2 border-green-300 bg-opacity-20 bg-green-300': volume === '1m', 'border-b border-gray-200 cursor-pointer': volume !== '1m'}">
<td class="px-6 py-4 text-sm leading-5 dark:text-gray-100" :class="{'font-bold': volume === '1m'}">1m</td>
<td class="px-6 py-4 text-sm leading-5 dark:text-gray-100" :class="{'font-bold': volume === '1m'}">
<span x-show="billingCycle === 'monthly'">$48</span>
<span x-show="billingCycle === 'yearly'">$32</span>
</td>
</tr>
<tr @click="volume = '2m'" :class="{'border-2 border-green-300 bg-green-50': volume === '2m', 'border-b border-gray-200 cursor-pointer': volume !== '1m'}">
<td class="px-6 py-4 text-sm leading-5" :class="{'font-bold': volume === '2m'}">2m</td>
<td class="px-6 py-4 text-sm leading-5" :class="{'font-bold': volume === '2m'}">
<tr @click="volume = '2m'" :class="{'border-2 border-green-300 bg-opacity-20 bg-green-300': volume === '2m', 'border-b border-gray-200 cursor-pointer': volume !== '1m'}">
<td class="px-6 py-4 text-sm leading-5 dark:text-gray-100" :class="{'font-bold': volume === '2m'}">2m</td>
<td class="px-6 py-4 text-sm leading-5 dark:text-gray-100" :class="{'font-bold': volume === '2m'}">
<span x-show="billingCycle === 'monthly'">$69</span>
<span x-show="billingCycle === 'yearly'">$46</span>
</td>
</tr>
<tr @click="volume = '5m'" :class="{'border-2 border-green-300 bg-green-50': volume === '5m', 'border-b border-gray-200 cursor-pointer': volume !== '1m'}">
<td class="px-6 py-4 text-sm leading-5" :class="{'font-bold': volume === '5m'}">5m</td>
<td class="px-6 py-4 text-sm leading-5" :class="{'font-bold': volume === '5m'}">
<tr @click="volume = '5m'" :class="{'border-2 border-green-300 bg-opacity-20 bg-green-300': volume === '5m', 'border-b border-gray-200 cursor-pointer': volume !== '1m'}">
<td class="px-6 py-4 text-sm leading-5 dark:text-gray-100" :class="{'font-bold': volume === '5m'}">5m</td>
<td class="px-6 py-4 text-sm leading-5 dark:text-gray-100" :class="{'font-bold': volume === '5m'}">
<span x-show="billingCycle === 'monthly'">$99</span>
<span x-show="billingCycle === 'yearly'">$66</span>
</td>
@ -110,7 +110,7 @@
Preview changes
</a>
<a x-show="window.plans[billingCycle][volume].product_id === '<%= @subscription.paddle_plan_id %>'" style="display: none;" tooltip="Select a different plan to continue" class="inline-flex items-center px-4 py-2 border border-transparent text-sm leading-5 font-medium rounded-md text-white bg-gray-400">
<a x-show="window.plans[billingCycle][volume].product_id === @subscription.paddle_plan_id %>'" style="display: none;" tooltip="Select a different plan to continue" class="inline-flex items-center px-4 py-2 border border-transparent text-sm leading-5 font-medium rounded-md text-white bg-gray-400">
<svg fill="currentColor" viewBox="0 0 20 20" class="w-4 h-4 inline mr-2"><path d="M10 12a2 2 0 100-4 2 2 0 000 4z"></path><path fill-rule="evenodd" d="M.458 10C1.732 5.943 5.522 3 10 3s8.268 2.943 9.542 7c-1.274 4.057-5.064 7-9.542 7S1.732 14.057.458 10zM14 10a4 4 0 11-8 0 4 4 0 018 0z" clip-rule="evenodd"></path></svg>
Preview changes
</a>
@ -119,7 +119,7 @@
</div>
</div>
<div class="text-center mt-8">
<div class="text-center mt-8 dark:text-gray-100">
Questions? Contact <%= link("support@plausible.io", to: "mailto: support@plausible.io", class: "text-indigo-500") %>
</div>

View File

@ -1,32 +1,32 @@
<div class="mx-auto mt-6 text-center">
<h1 class="text-3xl font-black">Confirm new subscription plan</h1>
<h1 class="text-3xl font-black dark:text-gray-100">Confirm new subscription plan</h1>
</div>
<div class="w-full max-w-lg px-4 mt-4 mx-auto">
<div class="flex-1 bg-white shadow-md rounded px-8 py-4 mb-4 mt-8">
<div class="text-lg font-bold">Due now</div>
<div class="block text-gray-500 text-sm">
<div class="flex-1 bg-white dark:bg-gray-800 shadow-md rounded px-8 py-4 mb-4 mt-8">
<div class="text-lg font-bold dark:text-gray-100">Due now</div>
<div class="block text-gray-500 dark:text-gray-200 text-sm">
Your card will be charged a pro-rated amount for the current billing period
</div>
<div class="flex flex-col mt-4">
<div class="-my-2 py-2 overflow-x-auto sm:-mx-6 sm:px-6 lg:-mx-8 lg:px-8">
<div class="align-middle inline-block min-w-full shadow overflow-hidden sm:rounded-lg border-b border-gray-200">
<div class="align-middle inline-block min-w-full shadow overflow-hidden sm:rounded-lg border-b border-gray-200 dark:border-t dark:border-l dark:border-r dark:shadow-none">
<table class="min-w-full">
<thead>
<tr>
<th class="px-6 py-3 border-b border-gray-200 bg-gray-100 text-left text-xs leading-4 font-medium text-gray-500 uppercase tracking-wider">
<th class="px-6 py-3 border-b border-gray-200 bg-gray-100 dark:bg-gray-900 text-left text-xs leading-4 font-medium text-gray-500 dark:text-gray-200 uppercase tracking-wider">
Amount
</th>
<th class="px-6 py-3 border-b border-gray-200 bg-gray-100 text-left text-xs leading-4 font-medium text-gray-500 uppercase tracking-wider">
<th class="px-6 py-3 border-b border-gray-200 bg-gray-100 dark:bg-gray-900 text-left text-xs leading-4 font-medium text-gray-500 dark:text-gray-200 uppercase tracking-wider">
Date
</th>
</tr>
</thead>
<tbody class="bg-white">
<tbody class="bg-white dark:bg-gray-800">
<tr class="border-b border-gray-200">
<td class="px-6 py-4 text-sm leading-5 font-bold">$<%= @preview_info["immediate_payment"]["amount"] %></td>
<td class="px-6 py-4 text-sm leading-5"><%= present_date(@preview_info["immediate_payment"]["date"]) %></td>
<td class="px-6 py-4 text-sm leading-5 font-bold dark:text-gray-100">$<%= @preview_info["immediate_payment"]["amount"] %></td>
<td class="px-6 py-4 text-sm leading-5 dark:text-gray-100"><%= present_date(@preview_info["immediate_payment"]["date"]) %></td>
</tr>
</tbody>
</table>
@ -41,22 +41,22 @@
<div class="flex flex-col">
<div class="-my-2 py-2 overflow-x-auto sm:-mx-6 sm:px-6 lg:-mx-8 lg:px-8">
<div class="align-middle inline-block min-w-full shadow overflow-hidden sm:rounded-lg border-b border-gray-200">
<div class="align-middle inline-block min-w-full shadow overflow-hidden sm:rounded-lg border-b border-gray-200 dark:border-t dark:border-l dark:border-r dark:shadow-none">
<table class="min-w-full">
<thead>
<tr>
<th class="px-6 py-3 border-b border-gray-200 bg-gray-100 text-left text-xs leading-4 font-medium text-gray-500 uppercase tracking-wider">
<th class="px-6 py-3 border-b border-gray-200 bg-gray-100 dark:bg-gray-900 text-left text-xs leading-4 font-medium text-gray-500 dark:text-gray-200 uppercase tracking-wider">
Amount
</th>
<th class="px-6 py-3 border-b border-gray-200 bg-gray-100 text-left text-xs leading-4 font-medium text-gray-500 uppercase tracking-wider">
<th class="px-6 py-3 border-b border-gray-200 bg-gray-100 dark:bg-gray-900 text-left text-xs leading-4 font-medium text-gray-500 dark:text-gray-200 uppercase tracking-wider">
Date
</th>
</tr>
</thead>
<tbody class="bg-white">
<tbody class="bg-white dark:bg-gray-800">
<tr class="border-b border-gray-200">
<td class="px-6 py-4 text-sm leading-5 font-bold">$<%= @preview_info["next_payment"]["amount"] %></td>
<td class="px-6 py-4 text-sm leading-5"><%= present_date(@preview_info["next_payment"]["date"]) %></td>
<td class="px-6 py-4 text-sm leading-5 font-bold dark:text-gray-100">$<%= @preview_info["next_payment"]["amount"] %></td>
<td class="px-6 py-4 text-sm leading-5 dark:text-gray-100"><%= present_date(@preview_info["next_payment"]["date"]) %></td>
</tr>
</tbody>
</table>
@ -66,18 +66,18 @@
<div class="flex items-center justify-between mt-10">
<span class="flex rounded-md shadow-sm">
<a href="/billing/change-plan" type="button" class="inline-flex items-center px-4 py-2 border border-gray-300 text-sm leading-5 font-medium rounded-md text-gray-700 bg-white hover:text-gray-500 focus:outline-none focus:border-blue-300 focus:ring active:text-gray-800 active:bg-gray-50 transition ease-in-out duration-150">
<a href="/billing/change-plan" type="button" class="inline-flex items-center px-4 py-2 border border-gray-300 dark:border-gray-500 text-sm leading-5 font-medium rounded-md text-gray-700 dark:text-gray-300 bg-white dark:bg-gray-800 hover:text-gray-500 dark:hover:text-gray-200 focus:outline-none focus:border-blue-300 focus:ring active:text-gray-800 dark:active:text-gray-200 active:bg-gray-50 transition ease-in-out duration-150">
Back
</a>
</span>
<span class="flex space-betwee rounded-md shadow-sm">
<%= button("Confirm plan change", to: "/billing/change-plan/#{@preview_info["plan_id"]}", method: :post, class: "inline-flex items-center px-4 py-2 border border-transparent text-sm leading-5 font-medium rounded-md text-white bg-indigo-600 hover:bg-indigo-500 focus:outline-none focus:border-indigo-700 focus:ring active:bg-indigo-700 transition ease-in-out duration-150") %>
<%= button("Confirm plan change", to: "/billing/change-plan/", method: :post, class: "inline-flex items-center px-4 py-2 border border-transparent text-sm leading-5 font-medium rounded-md text-white bg-indigo-600 hover:bg-indigo-500 focus:outline-none focus:border-indigo-700 focus:ring active:bg-indigo-700 transition ease-in-out duration-150") %>
</span>
</div>
</div>
</div>
<div class="text-center mt-8">
<div class="text-center mt-8 dark:text-gray-100">
Questions? Contact <%= link("support@plausible.io", to: "mailto: support@plausible.io", class: "text-indigo-500") %>
</div>

View File

@ -3,25 +3,25 @@
</script>
<div class="mx-auto mt-6 text-center">
<h1 class="text-3xl font-black">Upgrade your free trial</h1>
<h1 class="text-3xl font-black dark:text-gray-100">Upgrade your free trial</h1>
</div>
<div>
<div class="w-full max-w-4xl px-4 mt-4 mx-auto flex flex-col md:flex-row">
<div x-data="{planSize: '10k', billingCycle: 'monthly'}" class="flex-1 bg-white shadow-md rounded px-8 py-4 mb-4 mt-8">
<div class="w-full py-4">
<div x-data="{planSize: '10k', billingCycle: 'monthly'}" class="flex-1 bg-white dark:bg-gray-800 shadow-md rounded px-8 py-4 mb-4 mt-8">
<div class="w-full py-4 dark:text-gray-100">
<span>You've used <b><%= PlausibleWeb.AuthView.delimit_integer(@usage) %></b> pageviews in the last 30 days</span>
</div>
<div class="pt-2"></div>
<span class="relative z-0 inline-flex shadow-sm w-full">
<button type="button" @click="billingCycle = 'monthly'" :class="{'bg-indigo-600 text-white border-indigo-600': billingCycle === 'monthly', 'bg-white text-gray-700 hover:text-gray-500 border-gray-300': billingCycle === 'yearly'}" class="relative w-full text-center px-4 py-2 rounded-l-md border text-sm leading-5 font-medium focus:outline-none focus:border-blue-300 focus:ring transition ease-in-out duration-150">
<button type="button" @click="billingCycle = 'monthly'" :class="{'bg-indigo-600 text-white border-indigo-600': billingCycle === 'monthly', 'bg-white dark:bg-gray-800 text-gray-700 dark:text-gray-300 hover:text-gray-500 dark:hover:text-gray-200 border-gray-300 dark:border-gray-500': billingCycle === 'yearly'}" class="relative w-full text-center px-4 py-2 rounded-l-md border text-sm leading-5 font-medium focus:outline-none focus:border-blue-300 focus:ring transition ease-in-out duration-150">
Monthly billing
</button>
<button type="button" @click="billingCycle = 'yearly'" :class="{'bg-indigo-600 text-white border-indigo-600': billingCycle === 'yearly', 'bg-white text-gray-700 hover:text-gray-500 border-gray-300': billingCycle === 'monthly'}" class="-ml-px relative w-full text-center px-4 py-2 rounded-r-md border text-sm leading-5 font-medium focus:outline-none focus:border-blue-300 focus:ring transition ease-in-out duration-150">
<button type="button" @click="billingCycle = 'yearly'" :class="{'bg-indigo-600 text-white border-indigo-600': billingCycle === 'yearly', 'bg-white dark:bg-gray-800 text-gray-700 dark:text-gray-300 hover:text-gray-500 dark:hover:text-gray-200 border-gray-300 dark:border-gray-500': billingCycle === 'monthly'}" class="-ml-px relative w-full text-center px-4 py-2 rounded-r-md border text-sm leading-5 font-medium focus:outline-none focus:border-blue-300 focus:ring transition ease-in-out duration-150">
Yearly billing
<span class="inline-flex items-center px-2 py-0.5 rounded text-xs font-medium leading-4 bg-yellow-100 text-yellow-800">
<span class="inline-flex items-center px-2 py-0.5 rounded text-xs font-medium leading-4 bg-yellow-200 text-yellow-800 dark:text-yellow-900">
-33%
</span>
</button>
@ -31,64 +31,64 @@
<div class="flex flex-col">
<div class="-my-2 py-2 overflow-x-auto sm:-mx-6 sm:px-6 lg:-mx-8 lg:px-8">
<div class="align-middle inline-block min-w-full shadow overflow-hidden sm:rounded-lg border-b border-gray-200">
<div class="align-middle inline-block min-w-full shadow overflow-hidden sm:rounded-lg border-b border-gray-200 dark:border-t dark:border-l dark:border-r dark:shadow-none">
<table class="min-w-full">
<thead>
<tr>
<th class="px-6 py-3 border-b border-gray-200 bg-gray-100 text-left text-xs leading-4 font-medium text-gray-500 uppercase tracking-wider">
<th class="px-6 py-3 border-b border-gray-200 bg-gray-100 dark:bg-gray-900 text-left text-xs leading-4 font-medium text-gray-500 dark:text-gray-200 uppercase tracking-wider">
Monthly pageviews
</th>
<th class="px-6 py-3 border-b border-gray-200 bg-gray-100 text-left text-xs leading-4 font-medium text-gray-500 uppercase tracking-wider">
<th class="px-6 py-3 border-b border-gray-200 bg-gray-100 dark:bg-gray-900 text-left text-xs leading-4 font-medium text-gray-500 dark:text-gray-200 uppercase tracking-wider">
Price per month
</th>
</tr>
</thead>
<tbody class="bg-white">
<tr @click="planSize = '10k'" :class="{'border-2 border-green-300': planSize === '10k', 'border-b border-gray-200 cursor-pointer': planSize !== '10k'}">
<td class="px-6 py-4 text-sm leading-5" :class="{'font-bold': planSize === '10k'}">10k</td>
<td class="px-6 py-4 text-sm leading-5" :class="{'font-bold': planSize === '10k'}">
<tbody class="bg-white dark:bg-gray-800">
<tr @click="planSize = '10k'" :class="{'border-2 border-green-300 bg-opacity-20 bg-green-300': planSize === '10k', 'border-b border-gray-200 cursor-pointer': planSize !== '10k'}">
<td class="px-6 py-4 text-sm leading-5 dark:text-gray-100" :class="{'font-bold': planSize === '10k'}">10k</td>
<td class="px-6 py-4 text-sm leading-5 dark:text-gray-100" :class="{'font-bold': planSize === '10k'}">
<span x-show="billingCycle === 'monthly'">$6</span>
<span x-show="billingCycle === 'yearly'">$4</span>
</td>
</tr>
<tr @click="planSize = '100k'" :class="{'border-2 border-green-300': planSize === '100k', 'border-b border-gray-200 cursor-pointer': planSize !== '100k'}">
<td class="px-6 py-4 text-sm leading-5" :class="{'font-bold': planSize === '100k'}">100k</td>
<td class="px-6 py-4 text-sm leading-5" :class="{'font-bold': planSize === '100k'}">
<tr @click="planSize = '100k'" :class="{'border-2 border-green-300 bg-opacity-20 bg-green-300': planSize === '100k', 'border-b border-gray-200 cursor-pointer': planSize !== '100k'}">
<td class="px-6 py-4 text-sm leading-5 dark:text-gray-100" :class="{'font-bold': planSize === '100k'}">100k</td>
<td class="px-6 py-4 text-sm leading-5 dark:text-gray-100" :class="{'font-bold': planSize === '100k'}">
<span x-show="billingCycle === 'monthly'">$12</span>
<span x-show="billingCycle === 'yearly'">$8</span>
</td>
</tr>
<tr @click="planSize = '200k'" :class="{'border-2 border-green-300': planSize === '200k', 'border-b border-gray-200 cursor-pointer': planSize !== '200k'}">
<td class="px-6 py-4 text-sm leading-5" :class="{'font-bold': planSize === '200k'}">200k</td>
<td class="px-6 py-4 text-sm leading-5" :class="{'font-bold': planSize === '200k'}">
<tr @click="planSize = '200k'" :class="{'border-2 border-green-300 bg-opacity-20 bg-green-300': planSize === '200k', 'border-b border-gray-200 cursor-pointer': planSize !== '200k'}">
<td class="px-6 py-4 text-sm leading-5 dark:text-gray-100" :class="{'font-bold': planSize === '200k'}">200k</td>
<td class="px-6 py-4 text-sm leading-5 dark:text-gray-100" :class="{'font-bold': planSize === '200k'}">
<span x-show="billingCycle === 'monthly'">$18</span>
<span x-show="billingCycle === 'yearly'">$12</span>
</td>
</tr>
<tr @click="planSize = '500k'" :class="{'border-2 border-green-300': planSize === '500k', 'border-b border-gray-200 cursor-pointer': planSize !== '500k'}">
<td class="px-6 py-4 text-sm leading-5" :class="{'font-bold': planSize === '500k'}">500k</td>
<td class="px-6 py-4 text-sm leading-5" :class="{'font-bold': planSize === '500k'}">
<tr @click="planSize = '500k'" :class="{'border-2 border-green-300 bg-opacity-20 bg-green-300': planSize === '500k', 'border-b border-gray-200 cursor-pointer': planSize !== '500k'}">
<td class="px-6 py-4 text-sm leading-5 dark:text-gray-100" :class="{'font-bold': planSize === '500k'}">500k</td>
<td class="px-6 py-4 text-sm leading-5 dark:text-gray-100" :class="{'font-bold': planSize === '500k'}">
<span x-show="billingCycle === 'monthly'">$27</span>
<span x-show="billingCycle === 'yearly'">$18</span>
</td>
</tr>
<tr @click="planSize = '1m'" :class="{'border-2 border-green-300': planSize === '1m', 'border-b border-gray-200 cursor-pointer': planSize !== '1m'}">
<td class="px-6 py-4 text-sm leading-5" :class="{'font-bold': planSize === '1m'}">1m</td>
<td class="px-6 py-4 text-sm leading-5" :class="{'font-bold': planSize === '1m'}">
<tr @click="planSize = '1m'" :class="{'border-2 border-green-300 bg-opacity-20 bg-green-300': planSize === '1m', 'border-b border-gray-200 cursor-pointer': planSize !== '1m'}">
<td class="px-6 py-4 text-sm leading-5 dark:text-gray-100" :class="{'font-bold': planSize === '1m'}">1m</td>
<td class="px-6 py-4 text-sm leading-5 dark:text-gray-100" :class="{'font-bold': planSize === '1m'}">
<span x-show="billingCycle === 'monthly'">$48</span>
<span x-show="billingCycle === 'yearly'">$32</span>
</td>
</tr>
<tr @click="planSize = '2m'" :class="{'border-2 border-green-300': planSize === '2m', 'border-b border-gray-200 cursor-pointer': planSize !== '2m'}">
<td class="px-6 py-4 text-sm leading-5" :class="{'font-bold': planSize === '2m'}">2m</td>
<td class="px-6 py-4 text-sm leading-5" :class="{'font-bold': planSize === '2m'}">
<tr @click="planSize = '2m'" :class="{'border-2 border-green-300 bg-opacity-20 bg-green-300': planSize === '2m', 'border-b border-gray-200 cursor-pointer': planSize !== '2m'}">
<td class="px-6 py-4 text-sm leading-5 dark:text-gray-100" :class="{'font-bold': planSize === '2m'}">2m</td>
<td class="px-6 py-4 text-sm leading-5 dark:text-gray-100" :class="{'font-bold': planSize === '2m'}">
<span x-show="billingCycle === 'monthly'">$69</span>
<span x-show="billingCycle === 'yearly'">$46</span>
</td>
</tr>
<tr @click="planSize = '5m'" :class="{'border-2 border-green-300': planSize === '5m', 'border-b border-gray-200 cursor-pointer': planSize !== '5m'}">
<td class="px-6 py-4 text-sm leading-5" :class="{'font-bold': planSize === '5m'}">5m</td>
<td class="px-6 py-4 text-sm leading-5" :class="{'font-bold': planSize === '5m'}">
<tr @click="planSize = '5m'" :class="{'border-2 border-green-300 bg-opacity-20 bg-green-300': planSize === '5m', 'border-b border-gray-200 cursor-pointer': planSize !== '5m'}">
<td class="px-6 py-4 text-sm leading-5 dark:text-gray-100" :class="{'font-bold': planSize === '5m'}">5m</td>
<td class="px-6 py-4 text-sm leading-5 dark:text-gray-100" :class="{'font-bold': planSize === '5m'}">
<span x-show="billingCycle === 'monthly'">$99</span>
<span x-show="billingCycle === 'yearly'">$66</span>
</td>
@ -100,7 +100,7 @@
</div>
<div class="text-right mt-6">
<div class="mb-4 text-sm font-medium">Due today: <b x-text="window.plans[billingCycle][planSize].due_now">$6</b></div>
<div class="mb-4 text-sm font-medium dark:text-gray-100">Due today: <b x-text="window.plans[billingCycle][planSize].due_now">$6</b></div>
<span class="inline-flex rounded-md shadow-sm">
<button type="button" data-theme="none" :data-product="window.plans[billingCycle][planSize].product_id" data-email="<%= @conn.assigns[:current_user].email %>" data-disable-logout="true" data-passthrough="<%= @conn.assigns[:current_user].id %>" data-success="/billing/upgrade-success" class="paddle_button inline-flex items-center px-4 py-2 border border-transparent text-sm leading-5 font-medium rounded-md text-white bg-indigo-600 hover:bg-indigo-500 focus:outline-none focus:border-indigo-700 focus:ring active:bg-indigo-700 transition ease-in-out duration-150">
<svg fill="currentColor" viewBox="0 0 20 20" class="w-4 h-4 inline mr-2"><path fill-rule="evenodd" d="M5 9V7a5 5 0 0110 0v2a2 2 0 012 2v5a2 2 0 01-2 2H5a2 2 0 01-2-2v-5a2 2 0 012-2zm8-2v2H7V7a3 3 0 016 0z" clip-rule="evenodd"></path></svg>
@ -111,10 +111,10 @@
</div>
<div class="flex-1 pt-14 pl-8">
<h3 class="text-lg leading-6 font-medium text-gray-900">
<h3 class="text-lg leading-6 font-medium text-gray-900 dark:text-gray-100">
What happens if I go over my page views limit?
</h3>
<div class="mt-2 text-base leading-6 text-gray-500">
<div class="mt-2 text-base leading-6 text-gray-500 dark:text-gray-200">
You will never be charged extra for an occasional traffic spike. There are no surprise fees and your card will never be charged unexpectedly.<br /><br />
If your page views exceed your plan for two consecutive months, we will contact you to upgrade to a higher plan for the following month. You will have two weeks to make a decision. You can decide to continue with a higher plan or to cancel your account at that point.
</div>
@ -123,7 +123,7 @@
</div>
</div>
<div class="text-center mt-8">
<div class="text-center mt-8 dark:text-gray-100">
Questions? Contact <%= link("support@plausible.io", to: "mailto: support@plausible.io", class: "text-indigo-500") %>
</div>

View File

@ -1,10 +1,10 @@
<div class="w-full max-w-lg px-4 mt-4 mx-auto">
<div x-data="{volume: '10k', billingCycle: 'monthly'}" class="flex-1 bg-white shadow-md rounded px-8 py-4 mb-4 mt-8">
<div class="w-full pt-2 text-xl font-bold">
<div x-data="{volume: '10k', billingCycle: 'monthly'}" class="flex-1 bg-white dark:bg-gray-800 shadow-md rounded px-8 py-4 mb-4 mt-8">
<div class="w-full pt-2 text-xl font-bold dark:text-gray-100">
Subscription created succesfully
</div>
<div class="w-full text-gray-500 text-sm py-4">
<div class="w-full text-gray-500 dark:text-gray-200 text-sm py-4">
Thank you for upgrading your subscription. You will be redirected to your
account within 5 seconds.
</div>

View File

@ -8,11 +8,13 @@
<link rel="icon" type="image/png" sizes="32x32" href="<%= PlausibleWeb.Router.Helpers.static_path(@conn, "/images/icon/plausible_favicon.png") %>">
<title><%= assigns[:title] || "Plausible · Web analytics" %></title>
<link rel="stylesheet" href="<%= Routes.static_path(@conn, "/css/app.css") %>"/>
<script type="text/javascript" data-pref="<%= @conn.assigns[:current_user] && @conn.assigns[:current_user].theme %>" src="<%= Routes.static_path(@conn, "/js/applyTheme.js") %>"></script>
</head>
<body class="flex flex-col h-full bg-gray-100">
<body class="flex flex-col h-full bg-gray-100 dark:bg-gray-900">
<div class="container text-center mt-24">
<h1 class="text-5xl font-black"><%= @status %></h1>
<div class="my-4 text-xl"><%= @message %></div>
<h1 class="text-5xl font-black dark:text-gray-100"><%= @status %></h1>
<div class="my-4 text-xl dark:text-gray-100"><%= @message %></div>
<%= link("Go to the homepage", to: PlausibleWeb.LayoutView.home_dest(@conn), class: "button mt-4") %>
</div>
</body>

View File

@ -1,6 +1,6 @@
<%= if get_flash(@conn, :success) do %>
<div class="z-50 fixed inset-0 flex items-end justify-center px-4 py-6 pointer-events-none sm:p-6 sm:items-start sm:justify-end">
<div x-data="{ show: true }" x-show="show" x-init="setTimeout(() => show = false, 4000)" x-transition:enter="transform ease-out duration-300 transition" x-transition:enter-start="translate-y-2 opacity-0 sm:translate-y-0 sm:translate-x-2" x-transition:enter-end="translate-y-0 opacity-100 sm:translate-x-0" x-transition:leave="transition ease-in duration-100" x-transition:leave-start="opacity-100" x-transition:leave-end="opacity-0" class="max-w-sm w-full bg-white shadow-lg rounded-lg pointer-events-auto">
<div x-data="{ show: true }" x-show="show" x-init="setTimeout(() => show = false, 4000)" x-transition:enter="transform ease-out duration-300 transition" x-transition:enter-start="translate-y-2 opacity-0 sm:translate-y-0 sm:translate-x-2" x-transition:enter-end="translate-y-0 opacity-100 sm:translate-x-0" x-transition:leave="transition ease-in duration-100" x-transition:leave-start="opacity-100" x-transition:leave-end="opacity-0" class="max-w-sm w-full bg-white dark:bg-gray-800 shadow-lg rounded-lg pointer-events-auto">
<div class="rounded-lg ring-1 ring-black ring-opacity-5 overflow-hidden">
<div class="p-4">
<div class="flex items-start">
@ -10,15 +10,15 @@
</svg>
</div>
<div class="ml-3 w-0 flex-1 pt-0.5">
<p class="text-sm leading-5 font-medium text-gray-900">
<p class="text-sm leading-5 font-medium text-gray-900 dark:text-gray-100">
<%= get_flash(@conn, :success_title) || "Success!" %>
</p>
<p class="mt-1 text-sm leading-5 text-gray-500">
<p class="mt-1 text-sm leading-5 text-gray-500 dark:text-gray-200">
<%= get_flash(@conn, :success) %>
</p>
</div>
<div class="ml-4 flex-shrink-0 flex">
<button class="inline-flex text-gray-400 focus:outline-none focus:text-gray-500 transition ease-in-out duration-150" @click="show = false">
<button class="inline-flex text-gray-400 focus:outline-none focus:text-gray-500 dark:focus:text-gray-200 transition ease-in-out duration-150" @click="show = false">
<!-- Heroicon name: x -->
<svg class="h-5 w-5" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor">
<path fill-rule="evenodd" d="M4.293 4.293a1 1 0 011.414 0L10 8.586l4.293-4.293a1 1 0 111.414 1.414L11.414 10l4.293 4.293a1 1 0 01-1.414 1.414L10 11.414l-4.293 4.293a1 1 0 01-1.414-1.414L8.586 10 4.293 5.707a1 1 0 010-1.414z" clip-rule="evenodd" />
@ -34,7 +34,7 @@
<%= if get_flash(@conn, :error) do %>
<div class="z-50 fixed inset-0 flex items-end justify-center px-4 py-6 pointer-events-none sm:p-6 sm:items-start sm:justify-end">
<div x-data="{ show: true }" x-show="show" x-init="setTimeout(() => show = false, 4000)" x-transition:enter="transform ease-out duration-300 transition" x-transition:enter-start="translate-y-2 opacity-0 sm:translate-y-0 sm:translate-x-2" x-transition:enter-end="translate-y-0 opacity-100 sm:translate-x-0" x-transition:leave="transition ease-in duration-100" x-transition:leave-start="opacity-100" x-transition:leave-end="opacity-0" class="max-w-sm w-full bg-white shadow-lg rounded-lg pointer-events-auto">
<div x-data="{ show: true }" x-show="show" x-init="setTimeout(() => show = false, 4000)" x-transition:enter="transform ease-out duration-300 transition" x-transition:enter-start="translate-y-2 opacity-0 sm:translate-y-0 sm:translate-x-2" x-transition:enter-end="translate-y-0 opacity-100 sm:translate-x-0" x-transition:leave="transition ease-in duration-100" x-transition:leave-start="opacity-100" x-transition:leave-end="opacity-0" class="max-w-sm w-full bg-white dark:bg-gray-800 shadow-lg rounded-lg pointer-events-auto">
<div class="rounded-lg ring-1 ring-black ring-opacity-5 overflow-hidden">
<div class="p-4">
<div class="flex items-start">
@ -42,15 +42,15 @@
<svg class="w-6 h-6 text-red-400" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 8v4m0 4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"></path></svg>
</div>
<div class="ml-3 w-0 flex-1 pt-0.5">
<p class="text-sm leading-5 font-medium text-gray-900">
<p class="text-sm leading-5 font-medium text-gray-900 dark:text-gray-100">
<%= get_flash(@conn, :error_title) || "Error" %>
</p>
<p class="mt-1 text-sm leading-5 text-gray-500">
<p class="mt-1 text-sm leading-5 text-gray-500 dark:text-gray-200">
<%= get_flash(@conn, :error) %>
</p>
</div>
<div class="ml-4 flex-shrink-0 flex">
<button class="inline-flex text-gray-400 focus:outline-none focus:text-gray-500 transition ease-in-out duration-150" @click="show = false">
<button class="inline-flex text-gray-400 focus:outline-none focus:text-gray-500 dark:focus:text-gray-200 transition ease-in-out duration-150" @click="show = false">
<!-- Heroicon name: x -->
<svg class="h-5 w-5" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor">
<path fill-rule="evenodd" d="M4.293 4.293a1 1 0 011.414 0L10 8.586l4.293-4.293a1 1 0 111.414 1.414L11.414 10l4.293 4.293a1 1 0 01-1.414 1.414L10 11.414l-4.293 4.293a1 1 0 01-1.414-1.414L8.586 10 4.293 5.707a1 1 0 010-1.414z" clip-rule="evenodd" />

View File

@ -1,4 +1,4 @@
<div class="bg-gray-800 mt-12">
<div class="bg-gray-800 dark:bg-gray-800 mt-12">
<div class="container py-12 px-4 sm:px-6 lg:py-16 lg:px-8">
<div class="xl:grid xl:grid-cols-3 xl:gap-8">
<div class="my-8 xl:my-0">

View File

@ -1,5 +1,5 @@
<%= if is_current_tab(@conn, @this_tab) do %>
<a href="/<%= URI.encode_www_form(@site.domain) %>/settings/<%= @this_tab %>" class="flex items-center px-3 py-2 text-sm leading-5 font-medium text-gray-900 rounded-md bg-gray-100 hover:text-gray-900 hover:bg-gray-100 focus:outline-none focus:bg-gray-200 transition ease-in-out duration-150 <%= if @this_tab != "general", do: "mt-1" %>"><%= @text %></a>
<a href="/<%= URI.encode_www_form(@site.domain) %>/settings/<%= @this_tab %>" class="flex items-center px-3 py-2 text-sm leading-5 font-medium text-gray-900 dark:text-gray-100 rounded-md bg-gray-100 dark:bg-gray-900 hover:text-gray-900 hover:bg-gray-100 outline-none focus:outline-none focus:bg-gray-200 dark:focus:bg-gray-800 transition ease-in-out duration-150 cursor-default <%= if @this_tab != "general", do: "mt-1" %>"><%= @text %></a>
<% else %>
<a href="/<%= URI.encode_www_form(@site.domain) %>/settings/<%= @this_tab %>" class="flex items-center px-3 py-2 text-sm leading-5 font-medium text-gray-600 rounded-md hover:text-gray-900 hover:bg-gray-50 focus:outline-none focus:text-gray-900 focus:bg-gray-50 transition ease-in-out duration-150 <%= if @this_tab != "general", do: "mt-1" %>"><%= @text %></a>
<a href="/<%= URI.encode_www_form(@site.domain) %>/settings/<%= @this_tab %>" class="flex items-center px-3 py-2 text-sm leading-5 font-medium text-gray-600 dark:text-gray-400 rounded-md hover:text-gray-900 dark:hover:text-gray-100 hover:bg-gray-50 dark:hover:bg-gray-800 outline-none focus:outline-none focus:text-gray-900 focus:bg-gray-50 dark:focus:text-gray-100 dark:focus:bg-gray-800 transition ease-in-out duration-150 <%= if @this_tab != "general", do: "mt-1" %>"><%= @text %></a>
<% end %>

View File

@ -10,14 +10,18 @@
<title><%= assigns[:title] || "Plausible · Simple, privacy-friendly alternative to Google Analytics" %></title>
<link rel="stylesheet" href="<%= Routes.static_path(@conn, "/css/app.css") %>"/>
<%= render("_tracking.html", assigns) %>
<script type="text/javascript" data-pref="<%= @conn.assigns[:current_user] && @conn.assigns[:current_user].theme %>" src="<%= Routes.static_path(@conn, "/js/applyTheme.js") %>"></script>
</head>
<body class="flex flex-col h-full bg-gray-50">
<body class="flex flex-col h-full bg-gray-50 dark:bg-gray-850">
<nav class="relative py-8 z-10">
<div class="container">
<nav class="relative flex items-center justify-between sm:h-10 md:justify-center">
<div class="flex items-center flex-1 md:absolute md:inset-y-0 md:left-0">
<div class="flex items-center justify-between">
<a href="<%= home_dest(@conn) %>"><%= img_tag(PlausibleWeb.Router.Helpers.static_path(@conn, "/images/icon/plausible_logo.png"), class: "h-8 w-auto sm:h-10 -mt-2", alt: "Plausible logo") %></a>
<a href="<%= home_dest(@conn) %>">
<%= img_tag(PlausibleWeb.Router.Helpers.static_path(@conn, "/images/icon/plausible_logo_dark.png"), class: "h-8 w-auto sm:h-10 -mt-2 hidden dark:inline", alt: "Plausible logo")%>
<%= img_tag(PlausibleWeb.Router.Helpers.static_path(@conn, "/images/icon/plausible_logo.png"), class: "h-8 w-auto sm:h-10 -mt-2 inline dark:hidden", alt: "Plausible logo") %>
</a>
</div>
</div>
<div class="absolute flex items-center justify-end inset-y-0 right-0">
@ -26,23 +30,23 @@
<ul class="flex">
<%= if !Keyword.fetch!(Application.get_env(:plausible, :selfhost), :disable_subscription) && @conn.assigns[:current_user].subscription == nil do %>
<li class="mr-6 hidden sm:block">
<%= link(trial_notificaton(@conn.assigns[:current_user]), to: "/settings", class: "font-bold text-orange-900 rounded p-2 bg-orange-200", style: "line-height: 40px;") %>
<%= link(trial_notificaton(@conn.assigns[:current_user]), to: "/settings", class: "font-bold text-orange-900 dark:text-yellow-900 rounded p-2 bg-orange-200 dark:bg-yellow-100", style: "line-height: 40px;") %>
</li>
<% else %>
<li class="mr-6 hidden sm:block">
<%= link("Docs", to: "https://docs.plausible.io", class: "font-bold rounded p-2 hover:bg-gray-200", style: "line-height: 40px;", target: "_blank") %>
<%= link("Docs", to: "https://docs.plausible.io", class: "font-bold rounded p-2 hover:bg-gray-200 dark:hover:bg-gray-900 dark:text-gray-100", style: "line-height: 40px;", target: "_blank") %>
</li>
<% end %>
<li>
<div class="relative font-bold rounded">
<div data-dropdown-trigger class="flex items-center hover:bg-gray-200 rounded p-2 cursor-pointer">
<div data-dropdown-trigger class="flex items-center hover:bg-gray-200 dark:hover:bg-gray-900 rounded p-2 cursor-pointer dark:text-gray-100">
<span class="mr-2"><%= @conn.assigns[:current_user].name || @conn.assigns[:current_user].email %></span>
<svg style="height: 18px; transform: translateY(2px); fill: #606f7b;" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" x="0px" y="0px" viewBox="0 0 512 640" enable-background="new 0 0 512 512" xml:space="preserve"><g><circle cx="256" cy="52.8" r="50.8"/><circle cx="256" cy="256" r="50.8"/><circle cx="256" cy="459.2" r="50.8"/></g></svg>
</div>
<div data-dropdown style="top: 42px; right: 0px; width: 185px;" class="dropdown-content hidden absolute right-0 bg-white border border-gray-300 rounded shadow-md z-10">
<%= link("Settings", to: "/settings", class: "block py-2 px-2 border-b border-gray-300 hover:bg-gray-100") %>
<%= link("Log out", to: "/logout", method: :post, class: "block py-2 px-2 hover:bg-gray-100") %>
<div data-dropdown style="top: 42px; right: 0px; width: 185px;" class="dropdown-content hidden absolute right-0 bg-white dark:bg-gray-800 border border-gray-300 dark:border-gray-500 rounded shadow-md z-10">
<%= link("Settings", to: "/settings", class: "block py-2 px-2 border-b border-gray-300 dark:border-gray-500 hover:bg-gray-100 dark:hover:bg-gray-900 dark:text-gray-100") %>
<%= link("Log out", to: "/logout", method: :post, class: "block py-2 px-2 hover:bg-gray-100 dark:hover:bg-gray-900 dark:text-gray-100") %>
</div>
</div>
</li>
@ -59,7 +63,7 @@
<ul class="flex" x-show="!document.cookie.includes('logged_in=true')">
<li>
<div class="inline-flex">
<a href="/login" class="font-medium text-gray-500 hover:text-gray-900 focus:outline-none focus:text-gray-900 transition duration-150 ease-in-out">Login</a>
<a href="/login" class="font-medium text-gray-500 dark:text-gray-200 hover:text-gray-900 focus:outline-none focus:text-gray-900 transition duration-150 ease-in-out">Login</a>
</div>
</li>
</ul>
@ -67,7 +71,7 @@
<ul class="flex" x-show="!document.cookie.includes('logged_in=true')">
<li>
<div class="inline-flex">
<a href="/login" class="font-medium text-gray-500 hover:text-gray-900 focus:outline-none focus:text-gray-900 transition duration-150 ease-in-out">Login</a>
<a href="/login" class="font-medium text-gray-500 dark:text-gray-200 hover:text-gray-900 focus:outline-none focus:text-gray-900 transition duration-150 ease-in-out">Login</a>
</div>
<div class="inline-flex rounded shadow ml-6">
<a href="/register" class="inline-flex items-center justify-center px-5 py-2 border border-transparent text-base leading-6 font-medium rounded-md text-white bg-indigo-600 hover:bg-indigo-500 focus:outline-none focus:ring transition duration-150 ease-in-out">Sign up</a>
@ -102,7 +106,7 @@
</div>
<div class="order-3 mt-2 flex-shrink-0 w-full sm:order-2 sm:mt-0 sm:w-auto">
<div class="rounded-md shadow-sm">
<%= link("Update billing info", to: @conn.assigns[:current_user].subscription.update_url, class: "flex items-center justify-center px-4 py-2 border border-transparent text-sm leading-5 font-medium rounded-md text-gray-600 bg-white hover:text-gray-500 focus:outline-none focus:ring transition ease-in-out duration-150") %>
<%= link("Update billing info", to: @conn.assigns[:current_user].subscription.update_url, class: "flex items-center justify-center px-4 py-2 border border-transparent text-sm leading-5 font-medium rounded-md text-gray-600 dark:text-gray-400 bg-white dark:bg-gray-800 hover:text-gray-500 dark:hover:text-gray-200 focus:outline-none focus:ring transition ease-in-out duration-150") %>
</div>
</div>
</div>
@ -124,7 +128,7 @@
</div>
<div class="order-3 mt-2 flex-shrink-0 w-full sm:order-2 sm:mt-0 sm:w-auto">
<div class="rounded-md shadow-sm">
<%= link("Update billing info", to: @conn.assigns[:current_user].subscription.update_url, class: "flex items-center justify-center px-4 py-2 border border-transparent text-sm leading-5 font-medium rounded-md text-gray-600 bg-white hover:text-gray-500 focus:outline-none focus:ring transition ease-in-out duration-150") %>
<%= link("Update billing info", to: @conn.assigns[:current_user].subscription.update_url, class: "flex items-center justify-center px-4 py-2 border border-transparent text-sm leading-5 font-medium rounded-md text-gray-600 dark:text-gray-400 bg-white dark:bg-gray-800 hover:text-gray-500 dark:hover:text-gray-200 focus:outline-none focus:ring transition ease-in-out duration-150") %>
</div>
</div>
</div>

View File

@ -9,11 +9,13 @@
<title><%= assigns[:title] || "Plausible · Web analytics" %></title>
<link rel="stylesheet" href="<%= Routes.static_path(@conn, "/css/app.css") %>"/>
<%= render("_tracking.html", assigns) %>
<script type="text/javascript" data-pref="<%= @conn.assigns[:current_user] && @conn.assigns[:current_user].theme %>" src="<%= Routes.static_path(@conn, "/js/applyTheme.js") %>"></script>
</head>
<body class="flex flex-col h-full bg-gray-100">
<body class="flex flex-col h-full bg-gray-100 dark:bg-gray-900">
<div class="text-center w-full my-8">
<a href="<%= home_dest(@conn) %>">
<%= img_tag(PlausibleWeb.Router.Helpers.static_path(@conn, "/images/icon/plausible_logo.png"), class: "inline", style: "height: 2.5rem;", alt: "Plausible logo") %>
<%= img_tag(PlausibleWeb.Router.Helpers.static_path(@conn, "/images/icon/plausible_logo_dark.png"), class: "hidden dark:inline", style: "height: 2.5rem;", alt: "Plausible logo") %>
<%= img_tag(PlausibleWeb.Router.Helpers.static_path(@conn, "/images/icon/plausible_logo.png"), class: "inline dark:hidden", style: "height: 2.5rem;", alt: "Plausible logo")%>
</a>
</div>

View File

@ -1,15 +1,15 @@
<%= render_layout "app.html", assigns do %>
<div class="container pt-6">
<%= link("← Back to stats", to: "/#{URI.encode_www_form(@site.domain)}", class: "text-sm text-indigo-600 font-bold") %>
<div class="pb-5 border-b border-gray-200">
<h2 class="text-2xl font-bold leading-7 text-gray-900 sm:text-3xl sm:leading-9 sm:truncate">
<div class="pb-5 border-b border-gray-200 dark:border-gray-500">
<h2 class="text-2xl font-bold leading-7 text-gray-900 dark:text-gray-100 sm:text-3xl sm:leading-9 sm:truncate">
Settings for <%= @site.domain %>
</h2>
</div>
<div class="lg:grid lg:grid-cols-12 lg:gap-x-5 lg:mt-4">
<div class="py-4 g:py-0 lg:col-span-3">
<%= form_for @conn, "/sites/#{URI.encode_www_form(@site.domain)}/monthly-report/recipients", [class: "lg:hidden"], fn f -> %>
<%= select f, :tab, settings_tabs(), class: "mt-1 block w-full pl-3 pr-10 py-2 text-base border-gray-300 focus:outline-none focus:ring-indigo-500 focus:border-indigo-500 sm:text-sm rounded-md", onchange: "location.href = location.href.replace(/[^\/\/]*$/, event.target.value)", selected: List.last(@conn.path_info) %>
<%= select f, :tab, settings_tabs(), class: "dark:bg-gray-800 mt-1 block w-full pl-3 pr-10 py-2 text-base border-gray-300 dark:border-gray-500 outline-none focus:outline-none focus:ring-indigo-500 focus:border-indigo-500 sm:text-sm rounded-md dark:text-gray-100", onchange: "location.href = location.href.replace(/[^\/\/]*$/, event.target.value)", selected: List.last(@conn.path_info) %>
<% end %>
<div class="hidden lg:block">
<%= for [key: key, value: val] <- settings_tabs() do %>

View File

@ -1,3 +1,3 @@
<div class="relative bg-gray-50 overflow-hidden text-center">
<div class="relative bg-gray-50 dark:bg-gray-800 overflow-hidden text-center dark:text-gray-100">
You will be redirected... If it doesn't work, please click login.
</div>

View File

@ -1,14 +1,14 @@
<div class="max-w-md w-full mx-auto bg-white shadow-md rounded px-8 pt-6 pb-8 mb-4 mt-8">
<h2 class="text-xl font-black">DNS for <%= @site.custom_domain.domain %></h2>
<div class="max-w-md w-full mx-auto bg-white dark:bg-gray-800 shadow-md rounded px-8 pt-6 pb-8 mb-4 mt-8">
<h2 class="text-xl font-black dark:text-gray-100">DNS for <%= @site.custom_domain.domain %></h2>
<ol class="list-disc pl-4 my-4">
<li>Go to your DNS providers website</li>
<li class="mt-4">Create a new CNAME record for <code><%= @site.custom_domain.domain %></code></li>
<li class="mt-4">Point the record to <code>custom.<%= base_domain() %>.</code> (including the dot)</li>
<li class="mt-4">If you're using Cloudflare, make sure to disable the orange cloud proxy:</li>
<li class="dark:text-gray-100">Go to your DNS providers website</li>
<li class="mt-4 dark:text-gray-100">Create a new CNAME record for <code><%= @site.custom_domain.domain %></code></li>
<li class="mt-4 dark:text-gray-100">Point the record to <code>custom.<%= base_domain() %>.</code> (including the dot)</li>
<li class="mt-4 dark:text-gray-100">If you're using Cloudflare, make sure to disable the orange cloud proxy:</li>
<%= img_tag(Routes.static_path(@conn, "/images/cloudflare_orange_cloud.png"), class: "w-36 my-4 inline") %>
<span class="text-lg font-bold">&rarr;</span>
<span class="text-lg font-bold dark:text-gray-100">&rarr;</span>
<%= img_tag(Routes.static_path(@conn, "/images/cloudflare_gray_cloud.png"), class: "w-36 my-4 inline") %>
</ol>
<%= link("Done ->", to: "/sites/#{URI.encode_www_form(@site.domain)}/custom-domains/snippet", class: "button w-full mt-6") %>
<%= link("Done ", to: "/sites/#{URI.encode_www_form(@site.domain)}/custom-domains/snippet", class: "button w-full mt-6") %>
</div>

View File

@ -1,25 +1,25 @@
<%= form_for @conn, "/", [class: "max-w-md w-full mx-auto bg-white shadow-md rounded px-8 pt-6 pb-8 mb-4 mt-8"], fn f -> %>
<h2 class="text-xl font-black">Update javascript snippet</h2>
<%= form_for @conn, "/", [class: "max-w-md w-full mx-auto bg-white dark:bg-gray-800 shadow-md rounded px-8 pt-6 pb-8 mb-4 mt-8"], fn f -> %>
<h2 class="text-xl font-black dark:text-gray-100">Update javascript snippet</h2>
<div class="my-4">
<p>
<p class="dark:text-gray-100">
Allow up to 4 hours for DNS changes to propagate and for us to obtain an SSL certificate for <code><%= @site.custom_domain.domain %></code>
</p>
<p class="mt-4">
<p class="mt-4 dark:text-gray-100">
The setup is working when <a href="//<%= @site.custom_domain.domain %>/js/index.js" target="_blank" class="text-indigo-500"><%= @site.custom_domain.domain %>/js/index.js</a> loads the javascript tracker.
</p>
<p class="mt-4">
<p class="mt-4 dark:text-gray-100">
To finish your setup, please replace the tracking snippet on your site with the following.
</p>
<div class="relative">
<%= textarea f, :domain, id: "snippet_code", class: "transition overflow-hidden bg-gray-100 appearance-none border border-transparent rounded w-full p-2 pr-6 text-gray-700 leading-normal appearance-none focus:outline-none focus:bg-white focus:border-gray-300 text-xs mt-4 resize-none", value: snippet(@site), rows: 3, readonly: "readonly" %>
<%= textarea f, :domain, id: "snippet_code", class: "transition overflow-hidden bg-gray-100 dark:bg-gray-900 outline-none appearance-none border border-transparent rounded w-full p-2 pr-6 text-gray-700 dark:text-gray-300 leading-normal appearance-none focus:outline-none focus:bg-white dark:focus:bg-gray-900 focus:border-gray-300 dark:focus:border-gray-500 text-xs mt-4 resize-none", value: snippet(@site), rows: 3, readonly: "readonly" %>
<a onclick="var textarea = document.getElementById('snippet_code'); textarea.focus(); textarea.select(); document.execCommand('copy');" href="javascript:void(0)" class="no-underline text-indigo-500 text-sm hover:underline">
<svg class="absolute text-indigo-500" style="top: 24px; right: 12px;" xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="9" y="9" width="13" height="13" rx="2" ry="2"></rect><path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"></path></svg>
</a>
</div>
</div>
<%= link("Back to settings →", class: "button mt-4 w-full", to: "/#{URI.encode_www_form(@site.domain)}/settings/custom-domain") %>
<p class="mt-4 text-gray-600 text-sm">
Problems? <%= link("Get help via email", to: "mailto:support@plausible.io", class: "text-indigo-800 underline") %>
<p class="mt-4 text-gray-600 text-sm dark:text-gray-400">
Problems? <%= link("Get help via email", to: "mailto:support@plausible.io", class: "text-indigo-500 underline") %>
</p>
<% end %>

View File

@ -1,6 +1,6 @@
<div class="container pt-6">
<div class="pb-5 border-b border-gray-200 flex items-center justify-between">
<h2 class="text-2xl font-bold leading-7 text-gray-900 sm:text-3xl sm:leading-9 sm:truncate">
<div class="pb-5 border-b border-gray-200 dark:border-gray-500 flex items-center justify-between">
<h2 class="text-2xl font-bold leading-7 text-gray-900 dark:text-gray-100 sm:text-3xl sm:leading-9 sm:truncate">
My sites
</h2>
<a href="/sites/new" class="button w-full my-2 sm:my-0 sm:w-auto">+ Add a website</a>
@ -8,23 +8,23 @@
<ul class="my-6 grid grid-cols-1 gap-6 sm:grid-cols-2 lg:grid-cols-3">
<%= if Enum.empty?(@sites) do %>
<p>You don't have any sites yet</p>
<p class="dark:text-gray-100">You don't have any sites yet</p>
<% end %>
<%= for site <- @sites do %>
<div class="relative group">
<%= link(to: "/" <> URI.encode_www_form(site.domain)) do %>
<li class="col-span-1 bg-white rounded-lg shadow p-4 group-hover:shadow-lg cursor-pointer">
<li class="col-span-1 bg-white dark:bg-gray-800 rounded-lg shadow p-4 group-hover:shadow-lg cursor-pointer">
<div class="w-full flex items-center justify-between space-x-4">
<img src="https://icons.duckduckgo.com/ip3/<%= site.domain %>.ico" referrerpolicy="no-referrer" class="w-4 h-4 flex-shrink-0 mt-px">
<div class="flex-1 truncate -mt-px">
<h3 class="text-gray-900 font-medium text-lg truncate"><%= site.domain %></h3>
<h3 class="text-gray-900 font-medium text-lg truncate dark:text-gray-100"><%= site.domain %></h3>
</div>
</div>
<div class="pl-8 mt-2 flex items-center justify-between">
<span class="text-gray-600 text-sm truncate">
<span class="text-gray-800">
<b><%= PlausibleWeb.StatsView.large_number_format(Map.get(@visitors, site.domain, 0)) %></b> visitors in last 24h
<span class="text-gray-600 dark:text-gray-400 text-sm truncate">
<span class="text-gray-800 dark:text-gray-200">
<b><%= PlausibleWeb.StatsView.large_number_format(Map.get(@visitors, site.domain, 0)) %></b> visitor<%= if Map.get(@visitors, site.domain, 0) != 1 do %>s<% end %> in last 24h
</span>
</span>
</div>
@ -32,7 +32,7 @@
<% end %>
<%= link(to: "/" <> URI.encode_www_form(site.domain) <> "/settings", class: "absolute top-0 right-0 p-4 mt-1") do %>
<svg class="w-5 h-5 text-gray-600 opacity-0 group-hover:opacity-100 transition" fill="currentColor" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M11.49 3.17c-.38-1.56-2.6-1.56-2.98 0a1.532 1.532 0 01-2.286.948c-1.372-.836-2.942.734-2.106 2.106.54.886.061 2.042-.947 2.287-1.561.379-1.561 2.6 0 2.978a1.532 1.532 0 01.947 2.287c-.836 1.372.734 2.942 2.106 2.106a1.532 1.532 0 012.287.947c.379 1.561 2.6 1.561 2.978 0a1.533 1.533 0 012.287-.947c1.372.836 2.942-.734 2.106-2.106a1.533 1.533 0 01.947-2.287c1.561-.379 1.561-2.6 0-2.978a1.532 1.532 0 01-.947-2.287c.836-1.372-.734-2.942-2.106-2.106a1.532 1.532 0 01-2.287-.947zM10 13a3 3 0 100-6 3 3 0 000 6z" clip-rule="evenodd"></path></svg>
<svg class="w-5 h-5 text-gray-600 dark:text-gray-400 opacity-0 group-hover:opacity-100 transition hover:text-gray-900 dark:hover:text-gray-100" fill="currentColor" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M11.49 3.17c-.38-1.56-2.6-1.56-2.98 0a1.532 1.532 0 01-2.286.948c-1.372-.836-2.942.734-2.106 2.106.54.886.061 2.042-.947 2.287-1.561.379-1.561 2.6 0 2.978a1.532 1.532 0 01.947 2.287c-.836 1.372.734 2.942 2.106 2.106a1.532 1.532 0 012.287.947c.379 1.561 2.6 1.561 2.978 0a1.533 1.533 0 012.287-.947c1.372.836 2.942-.734 2.106-2.106a1.533 1.533 0 01.947-2.287c1.561-.379 1.561-2.6 0-2.978a1.532 1.532 0 01-.947-2.287c.836-1.372-.734-2.942-2.106-2.106a1.532 1.532 0 01-2.287-.947zM10 13a3 3 0 100-6 3 3 0 000 6z" clip-rule="evenodd"></path></svg>
<% end %>
</div>
<% end %>

View File

@ -1,22 +1,22 @@
<div class="w-full max-w-3xl mt-4 mx-auto flex">
<%= form_for @changeset, "/sites", [class: "max-w-lg w-full mx-auto bg-white shadow-lg rounded px-8 pt-6 pb-8 mb-4 mt-8"], fn f -> %>
<h2 class="text-xl font-black">Your website details</h2>
<%= form_for @changeset, "/sites", [class: "max-w-lg w-full mx-auto bg-white dark:bg-gray-800 shadow-lg rounded px-8 pt-6 pb-8 mb-4 mt-8"], fn f -> %>
<h2 class="text-xl font-black dark:text-gray-100">Your website details</h2>
<div class="my-6">
<%= label f, :domain, class: "block text-sm font-medium text-gray-700" %>
<%= label f, :domain, class: "block text-sm font-medium text-gray-700 dark:text-gray-300" %>
<div class="mt-2 flex rounded-md shadow-sm">
<span class="inline-flex items-center px-3 rounded-l-md border border-r-0 border-gray-300 bg-gray-50 text-gray-500 sm:text-sm">
<span class="inline-flex items-center px-3 rounded-l-md border border-r-0 border-gray-300 dark:border-gray-500 bg-gray-50 dark:bg-gray-850 text-gray-500 dark:text-gray-400 sm:text-sm">
https://
</span>
<%= text_input f, :domain, class: "focus:ring-indigo-500 focus:border-indigo-500 flex-1 block w-full px-3 py-2 rounded-none rounded-r-md sm:text-sm border-gray-300", placeholder: "example.com" %>
<%= text_input f, :domain, class: "focus:ring-indigo-500 focus:border-indigo-500 dark:bg-gray-800 flex-1 block w-full px-3 py-2 rounded-none rounded-r-md sm:text-sm border-gray-300 dark:border-gray-500 dark:bg-gray-900 dark:text-gray-300", placeholder: "example.com" %>
</div>
<%= error_tag f, :domain %>
</div>
<div class="my-6">
<%= label f, :timezone, "Reporting Timezone", class: "block text-sm font-medium text-gray-700" %>
<p class="text-gray-500 text-xs mt-1">To make sure we agree on what 'today' means</p>
<%= label f, :timezone, "Reporting Timezone", class: "block text-sm font-medium text-gray-700 dark:text-gray-300" %>
<p class="text-gray-500 dark:text-gray-400 text-xs mt-1">To make sure we agree on what 'today' means</p>
<div class="inline-block relative w-full">
<%= select f, :timezone, Plausible.Timezones.options(), id: "tz-select", selected: "Etc/Greenwich", class: "mt-1 block w-full pl-3 pr-10 py-2 text-base border-gray-300 focus:outline-none focus:ring-indigo-500 focus:border-indigo-500 sm:text-sm rounded-md" %>
<%= select f, :timezone, Plausible.Timezones.options(), id: "tz-select", selected: "Etc/Greenwich", class: "mt-1 block w-full pl-3 pr-10 py-2 text-base border-gray-300 dark:border-gray-500 dark:bg-gray-900 dark:text-gray-300 focus:outline-none focus:ring-indigo-500 focus:border-indigo-500 sm:text-sm rounded-md" %>
</div>
</div>
<script>

View File

@ -1,15 +1,15 @@
<%= form_for @changeset, "/sites/#{URI.encode_www_form(@site.domain)}/custom-domains", [class: "max-w-md w-full mx-auto bg-white shadow-md rounded px-8 pt-6 pb-8 mb-4 mt-8"], fn f -> %>
<h2 class="text-xl font-black">Setup custom domain</h2>
<div class="my-4">
<%= form_for @changeset, "/sites/#{URI.encode_www_form(@site.domain)}/custom-domains", [class: "max-w-md w-full mx-auto bg-white dark:bg-gray-800 shadow-md rounded px-8 pt-6 pb-8 mb-4 mt-8"], fn f -> %>
<h2 class="text-xl font-black dark:text-gray-100">Setup custom domain</h2>
<div class="my-4 dark:text-gray-100">
We recommend using a subdomain of the website you're running Plausible on.
If your site is on <code>example.com</code> you can use <code>stats.example.com</code>.
<br /><br /> The name of the subdomain can be anything, it doesn't have to be <code>stats</code>.
</div>
<div class="my-4">
<%= label f, :domain, class: "block text-sm font-bold" %>
<%= text_input f, :domain, class: "transition mt-3 bg-gray-100 appearance-none border border-transparent rounded w-full p-2 text-gray-700 leading-normal appearance-none focus:outline-none focus:bg-white focus:border-gray-300", placeholder: "stats.[yourdomain].com" %>
<%= label f, :domain, class: "block text-sm font-bold dark:text-gray-100" %>
<%= text_input f, :domain, class: "transition mt-3 bg-gray-100 dark:bg-gray-900 outline-none appearance-none border border-transparent rounded w-full p-2 text-gray-700 dark:text-gray-300 leading-normal focus:outline-none focus:bg-white dark:focus:bg-gray-800 focus:border-gray-300 dark:focus:border-gray-500", placeholder: "stats.[yourdomain].com" %>
<%= error_tag f, :domain %>
</div>
<%= submit "DNS setup ->", class: "button mt-4 w-full" %>
<%= submit "DNS setup ", class: "button mt-4 w-full dark:text-gray-100" %>
<% end %>

View File

@ -1,19 +1,19 @@
<%= form_for @changeset, "/#{URI.encode_www_form(@site.domain)}/goals", [class: "max-w-md w-full mx-auto bg-white shadow-md rounded px-8 pt-6 pb-8 mb-4 mt-8"], fn f -> %>
<h2 class="text-xl font-black">Add goal for <%= @site.domain %></h2>
<div class="mt-6 text-sm font-bold">Goal trigger</div>
<div class="my-3 w-full flex rounded border border-gray-300">
<div class="w-1/2 text-center py-2 border-r border-gray-300 shadow-inner font-bold bg-gray-100 cursor-pointer" id="pageview-tab">Pageview</div>
<div class="w-1/2 text-center py-2 bg-gray-100 cursor-pointer" id="event-tab">Custom event</div>
<%= form_for @changeset, "/#{URI.encode_www_form(@site.domain)}/goals", [class: "max-w-md w-full mx-auto bg-white dark:bg-gray-800 shadow-md rounded px-8 pt-6 pb-8 mb-4 mt-8"], fn f -> %>
<h2 class="text-xl font-black dark:text-gray-100">Add goal for <%= @site.domain %></h2>
<div class="mt-6 text-sm font-bold dark:text-gray-100">Goal trigger</div>
<div class="my-3 w-full flex rounded border border-gray-300 dark:border-gray-500">
<div class="w-1/2 text-center py-2 border-r border-gray-300 dark:border-gray-500 shadow-inner font-bold cursor-pointer text-white dark:text-gray-100 bg-indigo-600" id="pageview-tab">Pageview</div>
<div class="w-1/2 text-center py-2 cursor-pointer dark:text-gray-100" id="event-tab">Custom event</div>
</div>
<div class="my-6">
<div id="pageview-fields">
<%= label f, :page_path, class: "block text-sm font-bold" %>
<%= text_input f, :page_path, class: "transition mt-3 bg-gray-100 appearance-none border border-transparent rounded w-full p-2 text-gray-700 leading-normal appearance-none focus:outline-none focus:bg-white focus:border-gray-300", placeholder: "/success" %>
<%= label f, :page_path, class: "block text-sm font-bold dark:text-gray-100" %>
<%= text_input f, :page_path, class: "transition mt-3 bg-gray-100 dark:bg-gray-900 outline-none appearance-none border border-transparent rounded w-full p-2 text-gray-700 dark:text-gray-300 leading-normal focus:outline-none focus:bg-white dark:focus:bg-gray-800 focus:border-gray-300 dark:focus:border-gray-500", placeholder: "/success" %>
<%= error_tag f, :page_path %>
</div>
<div id="event-fields" class="hidden">
<%= label f, :event_name, class: "block text-sm font-bold" %>
<%= text_input f, :event_name, class: "transition mt-3 bg-gray-100 appearance-none border border-transparent rounded w-full p-2 text-gray-700 leading-normal appearance-none focus:outline-none focus:bg-white focus:border-gray-300", placeholder: "Signup" %>
<%= label f, :event_name, class: "block text-sm font-bold dark:text-gray-100" %>
<%= text_input f, :event_name, class: "transition mt-3 bg-gray-100 dark:bg-gray-900 outline-none appearance-none border border-transparent rounded w-full p-2 text-gray-700 dark:text-gray-300 leading-normal focus:outline-none focus:bg-white dark:focus:bg-gray-800 focus:border-gray-300 dark:focus:border-gray-500", placeholder: "Signup" %>
<%= error_tag f, :event_name %>
</div>
</div>
@ -24,14 +24,16 @@
<script>
document.getElementById('pageview-tab').onclick = function() {
document.getElementById('pageview-fields').classList.remove('hidden')
document.getElementById('pageview-tab').classList.add('shadow-inner', 'font-bold')
document.getElementById('pageview-tab').classList.add('shadow-inner', 'font-bold', 'bg-indigo-600', 'text-white')
document.getElementById('event-fields').classList.add('hidden')
document.getElementById('event-tab').classList.remove('shadow-inner', 'font-bold')
document.getElementById('event-tab').classList.remove('shadow-inner', 'font-bold', 'bg-indigo-600', 'text-white')
document.getElementById('event-tab').classList.add('dark:text-gray-100')
}
document.getElementById('event-tab').onclick = function() {
document.getElementById('event-fields').classList.remove('hidden')
document.getElementById('event-tab').classList.add('shadow-inner', 'font-bold')
document.getElementById('event-tab').classList.add('shadow-inner', 'font-bold', 'bg-indigo-600', 'text-white')
document.getElementById('pageview-fields').classList.add('hidden')
document.getElementById('pageview-tab').classList.remove('shadow-inner', 'font-bold')
document.getElementById('pageview-tab').classList.remove('shadow-inner', 'font-bold', 'bg-indigo-600', 'text-white')
document.getElementById('pageview-tab').classList.add('dark:text-gray-100')
}
</script>

View File

@ -1,12 +1,12 @@
<%= form_for @changeset, "/sites/#{URI.encode_www_form(@site.domain)}/shared-links", [class: "max-w-md w-full mx-auto bg-white shadow-md rounded px-8 pt-6 pb-8 mb-4 mt-8"], fn f -> %>
<h2 class="text-xl font-black">New shared link</h2>
<div class="my-4">
<%= form_for @changeset, "/sites/#{URI.encode_www_form(@site.domain)}/shared-links", [class: "max-w-md w-full mx-auto bg-white dark:bg-gray-800 shadow-md rounded px-8 pt-6 pb-8 mb-4 mt-8"], fn f -> %>
<h2 class="text-xl font-black dark:text-gray-100">New shared link</h2>
<div class="my-4 dark:text-gray-100">
Add a password or leave it blank so anyone with the link can see the stats.
Once the link is created, we cannot reveal the password. Please make sure you save it in a secure place.
</div>
<div class="my-6">
<%= label f, :password, "Password (optional)", class: "block text-sm font-bold" %>
<%= password_input f, :password, class: "transition mt-3 bg-gray-100 appearance-none border border-transparent rounded w-full p-2 text-gray-700 leading-normal appearance-none focus:outline-none focus:bg-white focus:border-gray-300" %>
<%= label f, :password, "Password (optional)", class: "block text-sm font-bold dark:text-gray-100" %>
<%= password_input f, :password, class: "transition mt-3 bg-gray-100 dark:bg-gray-900 outline-none appearance-none border border-transparent rounded w-full p-2 text-gray-700 dark:text-gray-300 leading-normal focus:outline-none focus:bg-white dark:focus:bg-gray-800 focus:border-gray-300 dark:focus:border-gray-500" %>
<%= error_tag f, :password %>
</div>

View File

@ -1,16 +1,18 @@
<div class="shadow bg-white sm:rounded-md sm:overflow-hidden py-6 px-4 sm:p-6">
<div class="shadow bg-white dark:bg-gray-800 sm:rounded-md sm:overflow-hidden py-6 px-4 sm:p-6">
<header class="relative">
<h2 class="text-lg leading-6 font-medium text-gray-900">Custom domain</h2>
<p class="mt-1 text-sm leading-5 text-gray-500">Serve the tracking script from your domain name as a first-party resource instead of loading the script from our domain.</p>
<h2 class="text-lg leading-6 font-medium text-gray-900 dark:text-gray-100">Custom domain</h2>
<p class="mt-1 text-sm leading-5 text-gray-500 dark:text-gray-200">Serve the tracking script from your domain name as a first-party resource instead of loading the script from our domain.</p>
<%= link(to: "https://docs.plausible.io/custom-domain/", target: "_blank") do %>
<svg class="w-6 h-6 absolute top-0 right-0 text-gray-400" fill="currentColor" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M18 10a8 8 0 11-16 0 8 8 0 0116 0zm-7-4a1 1 0 11-2 0 1 1 0 012 0zM9 9a1 1 0 000 2v3a1 1 0 001 1h1a1 1 0 100-2v-3a1 1 0 00-1-1H9z" clip-rule="evenodd"></path></svg>
<% end %>
</header>
<div class="mt-6">
<div class="mt-6 dark:text-gray-100 flex justify-between items-center">
<%= if @site.custom_domain do %>
Configured domain: <b><%= @site.custom_domain.domain %></b>
<%= link("Remove custom domain", to: "/sites/#{URI.encode_www_form(@site.domain)}/custom-domains/#{@site.custom_domain.id}", class: "inline-block mt-4 px-4 py-2 border border-gray-300 text-sm leading-5 font-medium rounded-md text-red-700 bg-white hover:text-red-500 focus:outline-none focus:border-blue-300 focus:ring active:text-red-800 active:bg-gray-50 transition ease-in-out duration-150", method: "delete") %>
<span>
Configured domain: <b><%= @site.custom_domain.domain %></b>
</span>
<%= link("Remove custom domain", to: "/sites/#{URI.encode_www_form(@site.domain)}/custom-domains/#{@site.custom_domain.id}", class: "inline-block mt-4 px-4 py-2 border border-gray-300 dark:border-gray-500 text-sm leading-5 font-medium rounded-md text-red-700 bg-white dark:bg-gray-800 hover:text-red-500 dark:hover:text-red-400 focus:outline-none focus:border-blue-300 focus:ring active:text-red-800 active:bg-gray-50 transition ease-in-out duration-150", method: "delete") %>
<% else %>
<%= link("Add custom domain", to: "/sites/#{URI.encode_www_form(@site.domain)}/custom-domains/new", class: "button") %>
<% end %>

View File

@ -1,32 +1,32 @@
<div class="sm:rounded-md sm:overflow-hidden shadow">
<div class="bg-white py-6 px-4 space-y-6 sm:p-6">
<div class="bg-white dark:bg-gray-800 py-6 px-4 space-y-6 sm:p-6">
<div>
<h2 class="text-lg leading-6 font-medium text-gray-900">Danger zone</h2>
<p class="mt-1 text-sm leading-5 text-gray-500">Desctructive actions below can result in irrecoverable data loss. Be careful.</p>
<h2 class="text-lg leading-6 font-medium text-gray-900 dark:text-gray-100">Danger zone</h2>
<p class="mt-1 text-sm leading-5 text-gray-500 dark:text-gray-200">Desctructive actions below can result in irrecoverable data loss. Be careful.</p>
</div>
<li class="py-4 flex items-center justify-between space-x-4">
<div class="flex flex-col">
<p class="text-sm leading-5 font-medium text-gray-900">
<p class="text-sm leading-5 font-medium text-gray-900 dark:text-gray-100">
Reset stats
</p>
<p class="text-sm leading-5 text-gray-500">
<p class="text-sm leading-5 text-gray-500 dark:text-gray-200">
Removes all pageviews but keeps the site configuration
</p>
</div>
<%= link("Reset #{@site.domain} stats", to: "/#{URI.encode_www_form(@site.domain)}/stats", method: :delete, class: "inline-block px-4 py-2 border border-gray-300 text-sm leading-5 font-medium rounded-md text-red-700 bg-white hover:text-red-500 focus:outline-none focus:border-blue-300 focus:ring active:text-red-800 active:bg-gray-50 transition ease-in-out duration-150", data: [confirm: "Resetting the stats cannot be reversed. Are you sure?"]) %>
<%= link("Reset #{@site.domain} stats", to: "/#{URI.encode_www_form(@site.domain)}/stats", method: :delete, class: "inline-block mt-4 px-4 py-2 border border-gray-300 dark:border-gray-500 text-sm leading-5 font-medium rounded-md text-red-700 bg-white dark:bg-gray-800 hover:text-red-500 dark:hover:text-red-400 focus:outline-none focus:border-blue-300 focus:ring active:text-red-800 active:bg-gray-50 transition ease-in-out duration-150", data: [confirm: "Resetting the stats cannot be reversed. Are you sure?"]) %>
</li>
<div class="border-b border-gray-200"></div>
<div class="border-b border-gray-200 dark:border-gray-500"></div>
<li class="py-4 flex items-center justify-between space-x-4">
<div class="flex flex-col">
<p class="text-sm leading-5 font-medium text-gray-900">
<p class="text-sm leading-5 font-medium text-gray-900 dark:text-gray-100">
Delete site
</p>
<p class="text-sm leading-5 text-gray-500">
<p class="text-sm leading-5 text-gray-500 dark:text-gray-200">
Removes all stats along with the site configuration
</p>
</div>
<%= link "Delete #{@site.domain}", to: "/#{URI.encode_www_form(@site.domain)}", method: :delete, class: "inline-block px-4 py-2 border border-transparent font-medium rounded-md text-red-700 bg-red-100 hover:bg-red-50 focus:outline-none focus:border-red-300 focus:ring active:bg-red-200 transition ease-in-out duration-150 sm:text-sm sm:leading-5", data: [confirm: "Deleting the site data cannot be reversed. Are you sure?"] %>
<%= link "Delete #{@site.domain}", to: "/#{URI.encode_www_form(@site.domain)}", method: :delete, class: "inline-block px-4 py-2 border border-transparent font-medium rounded-md text-red-700 dark:text-red-800 bg-red-100 dark:bg-red-200 hover:bg-red-50 dark:hover:bg-red-300 focus:outline-none focus:border-red-300 focus:ring active:bg-red-200 transition ease-in-out duration-150 sm:text-sm sm:leading-5", data: [confirm: "Deleting the site data cannot be reversed. Are you sure?"] %>
</li>
</div>
</div>

View File

@ -1,7 +1,7 @@
<div class="shadow bg-white sm:rounded-md sm:overflow-hidden py-6 px-4 sm:p-6">
<div class="shadow bg-white dark:bg-gray-800 sm:rounded-md sm:overflow-hidden py-6 px-4 sm:p-6">
<header class="relative">
<h2 class="text-lg leading-6 font-medium text-gray-900">Email reports</h2>
<p class="mt-1 text-sm leading-5 text-gray-500">Send weekly/monthly analytics reports to as many addresses as you wish</p>
<h2 class="text-lg leading-6 font-medium text-gray-900 dark:text-gray-100">Email reports</h2>
<p class="mt-1 text-sm leading-5 text-gray-500 dark:text-gray-200">Send weekly/monthly analytics reports to as many addresses as you wish</p>
<%= link(to: "https://docs.plausible.io/email-reports/", target: "_blank") do %>
<svg class="w-6 h-6 absolute top-0 right-0 text-gray-400" fill="currentColor" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M18 10a8 8 0 11-16 0 8 8 0 0116 0zm-7-4a1 1 0 11-2 0 1 1 0 012 0zM9 9a1 1 0 000 2v3a1 1 0 001 1h1a1 1 0 100-2v-3a1 1 0 00-1-1H9z" clip-rule="evenodd"></path></svg>
<% end %>
@ -10,20 +10,20 @@
<div class="my-8 flex items-center">
<%= if @weekly_report do %>
<%= button(to: "/sites/#{URI.encode_www_form(@site.domain)}/weekly-report/disable", method: :post, class: "bg-indigo-600 relative inline-flex flex-shrink-0 h-6 w-11 border-2 border-transparent rounded-full cursor-pointer transition-colors ease-in-out duration-200 focus:outline-none focus:ring") do %>
<span class="translate-x-5 inline-block h-5 w-5 rounded-full bg-white shadow transform transition ease-in-out duration-200"></span>
<span class="translate-x-5 inline-block h-5 w-5 rounded-full bg-white dark:bg-gray-800 shadow transform transition ease-in-out duration-200"></span>
<% end %>
<% else %>
<%= button(to: "/sites/#{URI.encode_www_form(@site.domain)}/weekly-report/enable", method: :post, class: "bg-gray-200 relative inline-flex flex-shrink-0 h-6 w-11 border-2 border-transparent rounded-full cursor-pointer transition-colors ease-in-out duration-200 focus:outline-none focus:ring") do %>
<span class="translate-x-0 inline-block h-5 w-5 rounded-full bg-white shadow transform transition ease-in-out duration-200"></span>
<%= button(to: "/sites/#{URI.encode_www_form(@site.domain)}/weekly-report/enable", method: :post, class: "bg-gray-200 dark:bg-gray-700 relative inline-flex flex-shrink-0 h-6 w-11 border-2 border-transparent rounded-full cursor-pointer transition-colors ease-in-out duration-200 focus:outline-none focus:ring") do %>
<span class="translate-x-0 inline-block h-5 w-5 rounded-full bg-white dark:bg-gray-800 shadow transform transition ease-in-out duration-200"></span>
<% end %>
<% end %>
<span class="ml-2">Send a weekly email report every Monday</span>
<span class="ml-2 dark:text-gray-100">Send a weekly email report every Monday</span>
</div>
<%= if @weekly_report do %>
<div class="text-sm text-gray-700 mt-6">
<h4 class="font-bold my-2">Weekly report recipients</h4>
<div class="text-sm text-gray-700 dark:text-gray-300 mt-6">
<h4 class="font-bold my-2 dark:text-gray-100">Weekly report recipients</h4>
<%= for recipient <- @weekly_report.recipients do %>
<div class="p-2 pl-3 flex justify-between bg-gray-100 rounded my-2 max-w-md">
<div class="p-2 pl-3 flex justify-between bg-gray-100 dark:bg-gray-900 rounded my-2 max-w-md">
<span>
<svg class="h-5 w-5 text-gray-400 inline mr-3" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true">
<path d="M2.003 5.884L10 9.882l7.997-3.998A2 2 0 0016 4H4a2 2 0 00-1.997 1.884z" />
@ -45,7 +45,7 @@
<path d="M18 8.118l-8 4-8-4V14a2 2 0 002 2h12a2 2 0 002-2V8.118z" />
</svg>
</div>
<%= email_input f, :recipient, class: "focus:ring-indigo-500 focus:border-indigo-500 block w-full rounded-none rounded-l-md pl-10 sm:text-sm border-gray-300", placeholder: "recipient@example.com" %>
<%= email_input f, :recipient, class: "focus:ring-indigo-500 dark:bg-gray-900 focus:border-indigo-500 block w-full rounded-none rounded-l-md pl-10 sm:text-sm border-gray-300 dark:border-gray-500 dark:placeholder-gray-400 dark:text-gray-100", placeholder: "recipient@example.com" %>
</div>
<%= submit class: "-ml-px relative button rounded-l-none" do %>
@ -57,24 +57,24 @@
<% end %>
</div>
<% end %>
<div class="my-8 border-b border-gray-300"></div>
<div class="my-8 border-b border-gray-300 dark:border-gray-500"></div>
<div class="my-8 flex items-center">
<%= if @monthly_report do %>
<%= button(to: "/sites/#{URI.encode_www_form(@site.domain)}/monthly-report/disable", method: :post, class: "bg-indigo-600 relative inline-flex flex-shrink-0 h-6 w-11 border-2 border-transparent rounded-full cursor-pointer transition-colors ease-in-out duration-200 focus:outline-none focus:ring") do %>
<span class="translate-x-5 inline-block h-5 w-5 rounded-full bg-white shadow transform transition ease-in-out duration-200"></span>
<span class="translate-x-5 inline-block h-5 w-5 rounded-full bg-white dark:bg-gray-800 shadow transform transition ease-in-out duration-200"></span>
<% end %>
<% else %>
<%= button(to: "/sites/#{URI.encode_www_form(@site.domain)}/monthly-report/enable", method: :post, class: "bg-gray-200 relative inline-flex flex-shrink-0 h-6 w-11 border-2 border-transparent rounded-full cursor-pointer transition-colors ease-in-out duration-200 focus:outline-none focus:ring") do %>
<span class="translate-x-0 inline-block h-5 w-5 rounded-full bg-white shadow transform transition ease-in-out duration-200"></span>
<%= button(to: "/sites/#{URI.encode_www_form(@site.domain)}/monthly-report/enable", method: :post, class: "bg-gray-200 dark:bg-gray-700 relative inline-flex flex-shrink-0 h-6 w-11 border-2 border-transparent rounded-full cursor-pointer transition-colors ease-in-out duration-200 focus:outline-none focus:ring") do %>
<span class="translate-x-0 inline-block h-5 w-5 rounded-full bg-white dark:bg-gray-800 shadow transform transition ease-in-out duration-200"></span>
<% end %>
<% end %>
<span class="ml-2">Send a monthly email report on 1st of the month</span>
<span class="ml-2 dark:text-gray-100">Send a monthly email report on 1st of the month</span>
</div>
<%= if @monthly_report do %>
<div class="text-sm text-gray-700 mt-6">
<div class="text-sm text-gray-700 dark:text-gray-300 mt-6">
<h4 class="font-bold my-2">Monthly report recipients</h4>
<%= for recipient <- @monthly_report.recipients do %>
<div class="p-2 pl-3 flex justify-between bg-gray-100 rounded my-2 max-w-md">
<div class="p-2 pl-3 flex justify-between bg-gray-100 dark:bg-gray-900 rounded my-2 max-w-md">
<span>
<svg class="h-5 w-5 text-gray-400 inline mr-3" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true">
<path d="M2.003 5.884L10 9.882l7.997-3.998A2 2 0 0016 4H4a2 2 0 00-1.997 1.884z" />
@ -96,7 +96,7 @@
<path d="M18 8.118l-8 4-8-4V14a2 2 0 002 2h12a2 2 0 002-2V8.118z" />
</svg>
</div>
<%= email_input f, :recipient, class: "focus:ring-indigo-500 focus:border-indigo-500 block w-full rounded-none rounded-l-md pl-10 sm:text-sm border-gray-300", placeholder: "recipient@example.com" %>
<%= email_input f, :recipient, class: "focus:ring-indigo-500 dark:bg-gray-900 focus:border-indigo-500 block w-full rounded-none rounded-l-md pl-10 sm:text-sm border-gray-300 dark:border-gray-500 dark:placeholder-gray-400 dark:text-gray-100", placeholder: "recipient@example.com" %>
</div>
<%= submit class: "-ml-px relative button rounded-l-none" do %>
@ -110,10 +110,10 @@
<% end %>
</div>
<div class="shadow bg-white sm:rounded-md sm:overflow-hidden py-6 px-4 sm:p-6">
<div class="shadow bg-white dark:bg-gray-800 sm:rounded-md sm:overflow-hidden py-6 px-4 sm:p-6">
<header class="relative">
<h2 class="text-lg leading-6 font-medium text-gray-900">Traffic spike notifications</h2>
<p class="mt-1 text-sm leading-5 text-gray-500">Get notified when your site has unusually high number of current visitors</p>
<h2 class="text-lg leading-6 font-medium text-gray-900 dark:text-gray-100">Traffic spike notifications</h2>
<p class="mt-1 text-sm leading-5 text-gray-500 dark:text-gray-200">Get notified when your site has unusually high number of current visitors</p>
<%= link(to: "https://docs.plausible.io/email-reports/", target: "_blank") do %>
<svg class="w-6 h-6 absolute top-0 right-0 text-gray-400" fill="currentColor" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M18 10a8 8 0 11-16 0 8 8 0 0116 0zm-7-4a1 1 0 11-2 0 1 1 0 012 0zM9 9a1 1 0 000 2v3a1 1 0 001 1h1a1 1 0 100-2v-3a1 1 0 00-1-1H9z" clip-rule="evenodd"></path></svg>
<% end %>
@ -122,18 +122,18 @@
<div class="my-8 flex items-center">
<%= if @spike_notification do %>
<%= button(to: "/sites/#{URI.encode_www_form(@site.domain)}/spike-notification/disable", method: :post, class: "bg-indigo-600 relative inline-flex flex-shrink-0 h-6 w-11 border-2 border-transparent rounded-full cursor-pointer transition-colors ease-in-out duration-200 focus:outline-none focus:ring") do %>
<span class="translate-x-5 inline-block h-5 w-5 rounded-full bg-white shadow transform transition ease-in-out duration-200"></span>
<span class="translate-x-5 inline-block h-5 w-5 rounded-full bg-white dark:bg-gray-800 shadow transform transition ease-in-out duration-200"></span>
<% end %>
<% else %>
<%= button(to: "/sites/#{URI.encode_www_form(@site.domain)}/spike-notification/enable", method: :post, class: "bg-gray-200 relative inline-flex flex-shrink-0 h-6 w-11 border-2 border-transparent rounded-full cursor-pointer transition-colors ease-in-out duration-200 focus:outline-none focus:ring") do %>
<span class="translate-x-0 inline-block h-5 w-5 rounded-full bg-white shadow transform transition ease-in-out duration-200"></span>
<%= button(to: "/sites/#{URI.encode_www_form(@site.domain)}/spike-notification/enable", method: :post, class: "bg-gray-200 dark:bg-gray-700 relative inline-flex flex-shrink-0 h-6 w-11 border-2 border-transparent rounded-full cursor-pointer transition-colors ease-in-out duration-200 focus:outline-none focus:ring") do %>
<span class="translate-x-0 inline-block h-5 w-5 rounded-full bg-white dark:bg-gray-800 shadow transform transition ease-in-out duration-200"></span>
<% end %>
<% end %>
<span class="ml-2">Send notifications of traffic spikes</span>
<span class="ml-2 dark:text-gray-100">Send notifications of traffic spikes</span>
</div>
<%= if @spike_notification do %>
<div class="text-sm text-gray-700 mt-6">
<div class="text-sm text-gray-700 dark:text-gray-300 mt-6">
<%= form_for Plausible.Site.SpikeNotification.changeset(@spike_notification, %{}), "/sites/#{URI.encode_www_form(@site.domain)}/spike-notification", fn f -> %>
<h4 class="font-bold my-2">Current visitor threshold</h4>
@ -145,16 +145,16 @@
<path d="M9 6a3 3 0 11-6 0 3 3 0 016 0zM17 6a3 3 0 11-6 0 3 3 0 016 0zM12.93 17c.046-.327.07-.66.07-1a6.97 6.97 0 00-1.5-4.33A5 5 0 0119 16v1h-6.07zM6 11a5 5 0 015 5v1H1v-1a5 5 0 015-5z" />
</svg>
</div>
<%= number_input f, :threshold, class: "focus:ring-indigo-500 focus:border-indigo-500 block w-full rounded-none rounded-l-md pl-10 sm:text-sm border-gray-300" %>
<%= number_input f, :threshold, class: "focus:ring-indigo-500 dark:bg-gray-900 focus:border-indigo-500 block w-full rounded-none rounded-l-md pl-10 sm:text-sm border-gray-300 dark:border-gray-500 dark:text-gray-100" %>
</div>
<button class="-ml-px relative button rounded-l-none">
<span>Save threshold</span>
</button>
</div>
<% end %>
<h4 class="font-bold mt-6">Notification recipients</h4>
<h4 class="font-bold mt-6 dark:text-gray-100">Notification recipients</h4>
<%= for recipient <- @spike_notification.recipients do %>
<div class="p-2 pl-3 flex justify-between bg-gray-100 rounded my-2 max-w-md">
<div class="p-2 pl-3 flex justify-between bg-gray-100 dark:bg-gray-900 rounded my-2 max-w-md">
<span>
<svg class="h-5 w-5 text-gray-400 inline mr-3" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true">
<path d="M2.003 5.884L10 9.882l7.997-3.998A2 2 0 0016 4H4a2 2 0 00-1.997 1.884z" />
@ -176,7 +176,7 @@
<path d="M18 8.118l-8 4-8-4V14a2 2 0 002 2h12a2 2 0 002-2V8.118z" />
</svg>
</div>
<%= email_input f, :recipient, class: "focus:ring-indigo-500 focus:border-indigo-500 block w-full rounded-none rounded-l-md pl-10 sm:text-sm border-gray-300", placeholder: "recipient@example.com" %>
<%= email_input f, :recipient, class: "focus:ring-indigo-500 dark:bg-gray-900 focus:border-indigo-500 block w-full rounded-none rounded-l-md pl-10 sm:text-sm border-gray-300 dark:border-gray-500 dark:placeholder-gray-400 dark:text-gray-100", placeholder: "recipient@example.com" %>
</div>
<%= submit class: "-ml-px relative button rounded-l-none" do %>

View File

@ -1,37 +1,37 @@
<%= form_for @changeset, "/#{URI.encode_www_form(@site.domain)}/settings", fn f -> %>
<div class="shadow sm:rounded-md sm:overflow-hidden">
<div class="bg-white py-6 px-4 space-y-6 sm:p-6">
<div class="bg-white dark:bg-gray-800 py-6 px-4 space-y-6 sm:p-6">
<header class="relative">
<h2 class="text-lg leading-6 font-medium text-gray-900">General information</h2>
<p class="mt-1 text-sm leading-5 text-gray-500">Update your reporting timezone.</p>
<h2 class="text-lg leading-6 font-medium text-gray-900 dark:text-gray-100">General information</h2>
<p class="mt-1 text-sm leading-5 text-gray-500 dark:text-gray-200">Update your reporting timezone.</p>
<%= link(to: "https://docs.plausible.io/general/", target: "_blank") do %>
<svg class="w-6 h-6 absolute top-0 right-0 text-gray-400" fill="currentColor" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M18 10a8 8 0 11-16 0 8 8 0 0116 0zm-7-4a1 1 0 11-2 0 1 1 0 012 0zM9 9a1 1 0 000 2v3a1 1 0 001 1h1a1 1 0 100-2v-3a1 1 0 00-1-1H9z" clip-rule="evenodd"></path></svg>
<% end %>
</header>
<div class="grid grid-cols-4 gap-6">
<div class="col-span-4 sm:col-span-2"> <%= label f, :domain, class: "block text-sm font-medium leading-5 text-gray-700" %>
<%= text_input f, :domain, class: "mt-1 block w-full shadow-sm focus:ring-indigo-500 focus:border-indigo-500 sm:max-w-xs sm:text-sm border-gray-300 rounded-md", disabled: "disabled" %>
<div class="col-span-4 sm:col-span-2"> <%= label f, :domain, class: "block text-sm font-medium leading-5 text-gray-700 dark:text-gray-300" %>
<%= text_input f, :domain, class: "dark:bg-gray-900 mt-1 block w-full shadow-sm focus:ring-indigo-500 focus:border-indigo-500 sm:max-w-xs sm:text-sm border-gray-300 dark:border-gray-500 rounded-md dark:text-gray-100", disabled: "disabled" %>
</div>
<div class="col-span-4 sm:col-span-2">
<%= label f, :timezone, "Reporting Timezone", class: "block text-sm font-medium leading-5 text-gray-700" %>
<%= select f, :timezone, Plausible.Timezones.options(), class: "mt-1 block w-full pl-3 pr-10 py-2 text-base border-gray-300 focus:outline-none focus:ring-indigo-500 focus:border-indigo-500 sm:text-sm rounded-md" %>
<%= label f, :timezone, "Reporting Timezone", class: "block text-sm font-medium leading-5 text-gray-700 dark:text-gray-300" %>
<%= select f, :timezone, Plausible.Timezones.options(), class: "dark:bg-gray-900 mt-1 block w-full pl-3 pr-10 py-2 text-base border-gray-300 dark:border-gray-500 focus:outline-none focus:ring-indigo-500 focus:border-indigo-500 sm:text-sm rounded-md dark:text-gray-100 cursor-pointer" %>
</div>
</div>
</div>
<div class="px-4 py-3 bg-gray-50 text-right sm:px-6">
<div class="px-4 py-3 bg-gray-50 dark:bg-gray-850 text-right sm:px-6">
<span class="inline-flex rounded-md shadow-sm">
<%= submit "Save", class: "bg-indigo-600 border border-transparent rounded-md py-2 px-4 inline-flex justify-center text-sm leading-5 font-medium text-white hover:bg-gray-700 focus:outline-none focus:border-gray-900 focus:ring active:bg-gray-900 transition duration-150 ease-in-out" %>
<%= submit "Save", class: "button" %>
</span>
</div>
</div>
<% end %>
<%= form_for @conn, "/", [class: "shadow bg-white sm:rounded-md sm:overflow-hidden py-6 px-4 sm:p-6"], fn f -> %>
<%= form_for @conn, "/", [class: "shadow bg-white dark:bg-gray-800 sm:rounded-md sm:overflow-hidden py-6 px-4 sm:p-6"], fn f -> %>
<header class="relative">
<h2 class="text-lg leading-6 font-medium text-gray-900">Javascript snippet</h2>
<p class="mt-1 text-sm leading-5 text-gray-500">Include this snippet in the <code>&lt;head&gt;</code> of your website.</p>
<h2 class="text-lg leading-6 font-medium text-gray-900 dark:text-gray-100">Javascript snippet</h2>
<p class="mt-1 text-sm leading-5 text-gray-500 dark:text-gray-200">Include this snippet in the <code>&lt;head&gt;</code> of your website.</p>
<%= link(to: "https://docs.plausible.io/plausible-script", target: "_blank") do %>
<svg class="w-6 h-6 absolute top-0 right-0 text-gray-400" fill="currentColor" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M18 10a8 8 0 11-16 0 8 8 0 0116 0zm-7-4a1 1 0 11-2 0 1 1 0 012 0zM9 9a1 1 0 000 2v3a1 1 0 001 1h1a1 1 0 100-2v-3a1 1 0 00-1-1H9z" clip-rule="evenodd"></path></svg>
@ -40,7 +40,7 @@
<div class="my-4">
<div class="relative">
<%= textarea f, :domain, id: "snippet_code", class: "transition overflow-hidden bg-gray-100 appearance-none border border-transparent rounded w-full p-2 pr-6 text-gray-700 leading-normal appearance-none focus:outline-none focus:bg-white focus:border-gray-300 text-xs mt-2 resize-none", value: snippet(@site), rows: 2 %>
<%= textarea f, :domain, id: "snippet_code", class: "transition overflow-hidden bg-gray-100 dark:bg-gray-900 outline-none appearance-none border border-transparent rounded w-full p-2 pr-6 text-gray-700 dark:text-gray-300 leading-normal focus:outline-none focus:bg-white focus:border-gray-300 dark:focus:border-gray-500 text-xs mt-2 resize-none", value: snippet(@site), rows: 2 %>
<a onclick="var textarea = document.getElementById('snippet_code'); textarea.focus(); textarea.select(); document.execCommand('copy');" href="javascript:void(0)" class="no-underline text-indigo-500 text-sm hover:underline">
<svg class="absolute text-indigo-500" style="top: 24px; right: 12px;" xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="9" y="9" width="13" height="13" rx="2" ry="2"></rect><path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"></path></svg>
</a>

View File

@ -1,7 +1,7 @@
<div class="shadow bg-white sm:rounded-md sm:overflow-hidden py-6 px-4 sm:p-6">
<div class="shadow bg-white dark:bg-gray-800 sm:rounded-md sm:overflow-hidden py-6 px-4 sm:p-6">
<header class="relative">
<h2 class="text-lg leading-6 font-medium text-gray-900">Goals</h2>
<p class="mt-1 text-sm leading-5 text-gray-500">Define actions that you want your users to take like visiting a certain page, submitting a form, etc.</p>
<h2 class="text-lg leading-6 font-medium text-gray-900 dark:text-gray-100">Goals</h2>
<p class="mt-1 text-sm leading-5 text-gray-500 dark:text-gray-200">Define actions that you want your users to take like visiting a certain page, submitting a form, etc.</p>
<%= link(to: "https://docs.plausible.io/goal-conversions/", target: "_blank") do %>
<svg class="w-6 h-6 absolute top-0 right-0 text-gray-400" fill="currentColor" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M18 10a8 8 0 11-16 0 8 8 0 0116 0zm-7-4a1 1 0 11-2 0 1 1 0 012 0zM9 9a1 1 0 000 2v3a1 1 0 001 1h1a1 1 0 100-2v-3a1 1 0 00-1-1H9z" clip-rule="evenodd"></path></svg>
<% end %>
@ -10,8 +10,8 @@
<%= if Enum.count(@goals) > 0 do %>
<div class="mt-4">
<%= for goal <- @goals do %>
<div class="border-b border-gray-300 py-3 flex justify-between">
<span class="text-sm font-medium text-gray-900"><%= goal_name(goal) %></span>
<div class="border-b border-gray-300 dark:border-gray-500 py-3 flex justify-between">
<span class="text-sm font-medium text-gray-900 dark:text-gray-100"><%= goal_name(goal) %></span>
<%= button(to: "/#{URI.encode_www_form(@site.domain)}/goals/#{goal.id}", method: :delete, class: "text-sm text-red-600", data: [confirm: "Are you sure you want to remove goal #{goal_name(goal)}? This will just affect the UI, all of your analytics data will stay intact."]) do %>
<svg class="feather feather-sm" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="3 6 5 6 21 6"></polyline><path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2"></path><line x1="10" y1="11" x2="10" y2="17"></line><line x1="14" y1="11" x2="14" y2="17"></line></svg>
<% end %>
@ -19,7 +19,7 @@
<% end %>
</div>
<% else %>
<div class="mt-4">No goals configured for this site yet</div>
<div class="mt-4 dark:text-gray-100">No goals configured for this site yet</div>
<% end %>
<%= link("+ Add goal", to: "/#{URI.encode_www_form(@site.domain)}/goals/new", class: "button mt-6") %>

View File

@ -1,7 +1,7 @@
<div class="shadow bg-white sm:rounded-md sm:overflow-hidden py-6 px-4 sm:p-6">
<div class="shadow bg-white dark:bg-gray-800 sm:rounded-md sm:overflow-hidden py-6 px-4 sm:p-6">
<header class="relative">
<h2 class="text-lg leading-6 font-medium text-gray-900">Google Search Console integration</h2>
<p class="mt-1 text-sm leading-5 text-gray-500">You can integrate with Google Search Console to get all of your important search results stats such as keyword phrases people find your site with.</p>
<h2 class="text-lg leading-6 font-medium text-gray-900 dark:text-gray-100">Google Search Console integration</h2>
<p class="mt-1 text-sm leading-5 text-gray-500 dark:text-gray-200">You can integrate with Google Search Console to get all of your important search results stats such as keyword phrases people find your site with.</p>
<%= link(to: "https://docs.plausible.io/google-search-console-integration/", target: "_blank") do %>
<svg class="w-6 h-6 absolute top-0 right-0 text-gray-400" fill="currentColor" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M18 10a8 8 0 11-16 0 8 8 0 0116 0zm-7-4a1 1 0 11-2 0 1 1 0 012 0zM9 9a1 1 0 000 2v3a1 1 0 001 1h1a1 1 0 100-2v-3a1 1 0 00-1-1H9z" clip-rule="evenodd"></path></svg>
<% end %>
@ -9,18 +9,18 @@
<%= if @site.google_auth do %>
<div class="py-2"></div>
<span class="text-gray-700">Linked Google account: <b><%= @site.google_auth.email %></b></span>
<span class="text-gray-700 dark:text-gray-300">Linked Google account: <b><%= @site.google_auth.email %></b></span>
<%= link("Unlink Google account", to: "/#{URI.encode_www_form(@site.domain)}/settings/google", class: "inline-block mt-4 px-4 py-2 border border-gray-300 text-sm leading-5 font-medium rounded-md text-red-700 bg-white hover:text-red-500 focus:outline-none focus:border-blue-300 focus:ring active:text-red-800 active:bg-gray-50 transition ease-in-out duration-150", method: "delete") %>
<%= link("Unlink Google account", to: "/#{URI.encode_www_form(@site.domain)}/settings/google", class: "inline-block mt-4 px-4 py-2 border border-gray-300 dark:border-gray-500 text-sm leading-5 font-medium rounded-md text-red-700 bg-white dark:bg-gray-800 hover:text-red-500 dark:hover:text-red-400 focus:outline-none focus:border-blue-300 focus:ring active:text-red-800 active:bg-gray-50 transition ease-in-out duration-150", method: "delete") %>
<%= case @search_console_domains do %>
<% {:ok, domains} -> %>
<%= if @site.google_auth.property && !(@site.google_auth.property in domains) do %>
<p class="text-gray-700 mt-6 font-bold">
<p class="text-gray-700 dark:text-gray-300 mt-6 font-bold">
NB: Your Google account does not have access to your currently configured property, <%= @site.google_auth.property %>. Please select a verified property from the list below.
</p>
<% else %>
<p class="text-gray-700 mt-6">
<p class="text-gray-700 dark:text-gray-300 mt-6">
Select the Google Search Console property you would like to pull keyword data from. If you don't see your domain, <%= link("set it up and verify", to: "https://docs.plausible.io/google-search-console-integration", class: "text-indigo-500") %> on Search Console first.
</p>
<% end %>
@ -35,13 +35,13 @@
<%= submit "Save", class: "button" %>
<% end %>
<% {:error, error} -> %>
<p class="text-gray-700 mt-6">The following error happened when fetching your Google Search Console domains.</p>
<p class="text-gray-700 dark:text-gray-300 mt-6">The following error happened when fetching your Google Search Console domains.</p>
<p class="text-red-700 font-medium mt-3"><%= error %></p>
<% end %>
<% else %>
<%= button("Continue with Google", to: Plausible.Google.Api.authorize_url(@site.id), class: "button mt-8") %>
<div class="text-gray-700 mt-8">
<div class="text-gray-700 dark:text-gray-300 mt-8">
NB: You also need to set up your site on <%= link("Google Search Console", to: "https://search.google.com/search-console/about") %> for the integration to work. <%= link("Read the docs", to: "https://docs.plausible.io/google-search-console-integration", class: "text-indigo-500") %>
</div>
<% end %>

View File

@ -1,7 +1,7 @@
<div class="shadow bg-white sm:rounded-md sm:overflow-hidden py-6 px-4 sm:p-6">
<div class="shadow bg-white dark:bg-gray-800 sm:rounded-md sm:overflow-hidden py-6 px-4 sm:p-6">
<header class="relative">
<h2 class="text-lg leading-6 font-medium text-gray-900">Public dashboard</h2>
<p class="mt-1 text-sm leading-5 text-gray-500">Share your stats publicly or keep them private</p>
<h2 class="text-lg leading-6 font-medium text-gray-900 dark:text-gray-100">Public dashboard</h2>
<p class="mt-1 text-sm leading-5 text-gray-500 dark:text-gray-200">Share your stats publicly or keep them private</p>
<%= link(to: "https://docs.plausible.io/visibility", target: "_blank") do %>
<svg class="w-6 h-6 absolute top-0 right-0 text-gray-400" fill="currentColor" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M18 10a8 8 0 11-16 0 8 8 0 0116 0zm-7-4a1 1 0 11-2 0 1 1 0 012 0zM9 9a1 1 0 000 2v3a1 1 0 001 1h1a1 1 0 100-2v-3a1 1 0 00-1-1H9z" clip-rule="evenodd"></path></svg>
<% end %>
@ -10,24 +10,24 @@
<%= if @site.public do %>
<div class="flex items-center space-x-3 mt-4">
<%= button(to: "/sites/#{URI.encode_www_form(@site.domain)}/make-private", method: "POST", class: "bg-indigo-600 relative inline-flex flex-shrink-0 h-6 w-11 border-2 border-transparent rounded-full cursor-pointer transition-colors ease-in-out duration-200 focus:outline-none focus:ring") do %>
<span class="translate-x-5 inline-block h-5 w-5 rounded-full bg-white shadow transform transition ease-in-out duration-200"></span>
<span class="translate-x-5 inline-block h-5 w-5 rounded-full bg-white dark:bg-gray-800 shadow transform transition ease-in-out duration-200"></span>
<% end %>
<span class="text-sm leading-5 font-medium text-gray-900">Make stats publicly available on <a href="<%= plausible_url() <> "/" <> URI.encode_www_form(@site.domain)%>" class="text-indigo-500"><%= plausible_url() <> "/" <> URI.encode_www_form(@site.domain)%></a></span>
<span class="text-sm leading-5 font-medium text-gray-900 dark:text-gray-100">Make stats publicly available on <a href="<%= plausible_url() <> "/" <> URI.encode_www_form(@site.domain)%>" class="text-indigo-500"><%= plausible_url() <> "/" <> URI.encode_www_form(@site.domain)%></a></span>
</div>
<% else %>
<div class="flex items-center space-x-3 mt-4">
<%= button(to: "/sites/#{URI.encode_www_form(@site.domain)}/make-public", method: "POST", class: "bg-gray-200 relative inline-flex flex-shrink-0 h-6 w-11 border-2 border-transparent rounded-full cursor-pointer transition-colors ease-in-out duration-200 focus:outline-none focus:ring") do %>
<span class="translate-x-0 inline-block h-5 w-5 rounded-full bg-white shadow transform transition ease-in-out duration-200"></span>
<%= button(to: "/sites/#{URI.encode_www_form(@site.domain)}/make-public", method: "POST", class: "bg-gray-200 dark:bg-gray-700 relative inline-flex flex-shrink-0 h-6 w-11 border-2 border-transparent rounded-full cursor-pointer transition-colors ease-in-out duration-200 focus:outline-none focus:ring") do %>
<span class="translate-x-0 inline-block h-5 w-5 rounded-full bg-white dark:bg-gray-800 shadow transform transition ease-in-out duration-200"></span>
<% end %>
<span class="text-sm leading-5 font-medium text-gray-900">Make stats publicly available on <a href="<%= plausible_url() <> "/" <> URI.encode_www_form(@site.domain)%>" class="text-indigo-500"><%= plausible_url() <> "/" <> URI.encode_www_form(@site.domain)%></a></span>
<span class="text-sm leading-5 font-medium text-gray-900 dark:text-gray-100">Make stats publicly available on <a href="<%= plausible_url() <> "/" <> URI.encode_www_form(@site.domain)%>" class="text-indigo-500"><%= plausible_url() <> "/" <> URI.encode_www_form(@site.domain)%></a></span>
</div>
<% end %>
</div>
<div class="shadow bg-white sm:rounded-md sm:overflow-hidden py-6 px-4 sm:p-6">
<div class="shadow bg-white dark:bg-gray-800 sm:rounded-md sm:overflow-hidden py-6 px-4 sm:p-6">
<header class="relative">
<h2 class="text-lg leading-6 font-medium text-gray-900">Shared links</h2>
<p class="mt-1 text-sm leading-5 text-gray-500">You can share your stats privately by generating a shared link. The links are impossible to guess and you can add password protection for extra security.</p>
<h2 class="text-lg leading-6 font-medium text-gray-900 dark:text-gray-100">Shared links</h2>
<p class="mt-1 text-sm leading-5 text-gray-500 dark:text-gray-200">You can share your stats privately by generating a shared link. The links are impossible to guess and you can add password protection for extra security.</p>
<%= link(to: "https://docs.plausible.io/shared-links", target: "_blank") do %>
<svg class="w-6 h-6 absolute top-0 right-0 text-gray-400" fill="currentColor" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M18 10a8 8 0 11-16 0 8 8 0 0116 0zm-7-4a1 1 0 11-2 0 1 1 0 012 0zM9 9a1 1 0 000 2v3a1 1 0 001 1h1a1 1 0 100-2v-3a1 1 0 00-1-1H9z" clip-rule="evenodd"></path></svg>
<% end %>
@ -36,11 +36,11 @@
<div class="mt-6">
<%= for link <- @shared_links do %>
<div class="flex relative w-full max-w-xl mt-2 text-sm">
<input type="text" id="<%= link.slug %>" readonly="readonly" value="<%= shared_link_dest(link) %>" class="transition bg-gray-100 appearance-none border border-transparent rounded rounded-r-none w-full p-2 text-gray-700 appearance-none focus:outline-none focus:border-gray-300" />
<button onclick="var input = document.getElementById('<%= link.slug %>'); input.focus(); input.select(); document.execCommand('copy');" href="javascript:void(0)" class="py-2 px-4 bg-gray-100 text-indigo-800 rounded-none border-r border-gray-300">
<input type="text" id="<%= link.slug %>" readonly="readonly" value="<%= shared_link_dest(link) %>" class="transition bg-gray-100 dark:bg-gray-900 outline-none appearance-none border border-transparent rounded rounded-r-none w-full p-2 text-gray-700 dark:text-gray-300 focus:outline-none focus:border-gray-300 dark:focus:border-gray-500" />
<button onclick="var input = document.getElementById('<%= link.slug %>'); input.focus(); input.select(); document.execCommand('copy');" href="javascript:void(0)" class="py-2 px-4 bg-gray-200 dark:bg-gray-850 text-indigo-800 dark:text-indigo-500 rounded-none border-r border-gray-300 dark:border-gray-500 hover:bg-gray-300 dark:hover:bg-gray-825">
<svg class="feather-sm" xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="9" y="9" width="13" height="13" rx="2" ry="2"></rect><path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"></path></svg>
</button>
<%= button(to: "/sites/#{URI.encode_www_form(@site.domain)}/shared-links/#{link.slug}", method: :delete, class: "py-2 px-4 bg-gray-100 text-red-600 rounded-l-none", data: [confirm: "Are you sure you want to delete this shared link? The stats will not be accessible with this link anymore."]) do %>
<%= button(to: "/sites/#{URI.encode_www_form(@site.domain)}/shared-links/#{link.slug}", method: :delete, class: "py-2 px-4 bg-gray-200 dark:bg-gray-850 text-red-600 dark:text-red-500 rounded-l-none hover:bg-gray-300 dark:hover:bg-gray-825", data: [confirm: "Are you sure you want to delete this shared link? The stats will not be accessible with this link anymore."]) do %>
<svg class="feather feather-sm" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="3 6 5 6 21 6"></polyline><path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2"></path><line x1="10" y1="11" x2="10" y2="17"></line><line x1="14" y1="11" x2="14" y2="17"></line></svg>
<% end %>
</div>

View File

@ -1,11 +1,11 @@
<div class="w-full max-w-3xl mt-4 mx-auto flex">
<%= form_for @conn, "/", [class: "max-w-lg w-full mx-auto bg-white shadow-md rounded px-8 pt-6 pb-8 mb-4 mt-8"], fn f -> %>
<h2 class="text-xl font-bold">Add javascript snippet</h2>
<%= form_for @conn, "/", [class: "max-w-lg w-full mx-auto bg-white dark:bg-gray-800 shadow-md rounded px-8 pt-6 pb-8 mb-4 mt-8"], fn f -> %>
<h2 class="text-xl font-bold dark:text-gray-100">Add javascript snippet</h2>
<div class="my-4">
<p>Paste this snippet in the <code>&lt;head&gt;</code> of your website.</p>
<p class="dark:text-gray-100">Paste this snippet in the <code>&lt;head&gt;</code> of your website.</p>
<div class="relative">
<%= textarea f, :domain, id: "snippet_code", class: "transition overflow-hidden bg-gray-100 appearance-none border border-transparent rounded w-full p-2 pr-6 text-gray-700 leading-normal appearance-none focus:outline-none focus:bg-white focus:border-gray-400 text-xs mt-4 resize-none", value: snippet(@site), rows: 3, readonly: "readonly" %>
<%= textarea f, :domain, id: "snippet_code", class: "transition overflow-hidden bg-gray-100 dark:bg-gray-900 appearance-none border border-transparent rounded w-full p-2 pr-6 text-gray-700 dark:text-gray-300 leading-normal appearance-none focus:outline-none focus:bg-white dark:focus:bg-gray-800 focus:border-gray-400 dark:focus:border-gray-500 text-xs mt-4 resize-none", value: snippet(@site), rows: 3, readonly: "readonly" %>
<a onclick="var textarea = document.getElementById('snippet_code'); textarea.focus(); textarea.select(); document.execCommand('copy');" href="javascript:void(0)" class="no-underline text-indigo-500 text-sm hover:underline">
<svg class="absolute text-indigo-500" style="top: 24px; right: 12px;" xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="9" y="9" width="13" height="13" rx="2" ry="2"></rect><path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"></path></svg>
</a>

View File

@ -1,12 +1,12 @@
<%= form_for @conn, "/share/#{@link.slug}/authenticate", [class: "max-w-md w-full mx-auto bg-white shadow-md rounded px-8 pt-6 pb-8 mb-4 mt-8"], fn f -> %>
<h2 class="text-xl font-black">Enter password</h2>
<div class="my-4">
<%= form_for @conn, "/share/#{@link.slug}/authenticate", [class: "max-w-md w-full mx-auto bg-white dark:bg-gray-800 shadow-md rounded px-8 pt-6 pb-8 mb-4 mt-8"], fn f -> %>
<h2 class="text-xl font-black dark:text-gray-100">Enter password</h2>
<div class="my-4 dark:text-gray-100">
This link is password-protected. Please enter the password to continue to the dashboard.
</div>
<div class="my-6">
<%= label f, :password, "Password", class: "block text-sm font-bold" %>
<%= password_input f, :password, class: "transition mt-3 bg-gray-100 appearance-none border border-transparent rounded w-full p-2 text-gray-700 leading-normal appearance-none focus:outline-none focus:bg-white focus:border-gray-300" %>
<%= label f, :password, "Password", class: "block text-sm font-bold dark:text-gray-100" %>
<%= password_input f, :password, class: "transition mt-3 bg-gray-100 dark:bg-gray-900 outline-none appearance-none border border-transparent rounded w-full p-2 text-gray-700 dark:text-gray-300 leading-normal focus:outline-none focus:bg-white dark:focus:bg-gray-800 focus:border-gray-300 dark:focus:border-gray-500" %>
<%= if @conn.assigns[:error] do %>
<div class="text-red-500 text-xs italic mt-4"><%= @conn.assigns[:error] %></div>

View File

@ -8,9 +8,9 @@
<div id="stats-react-container" data-domain="<%= @site.domain %>" data-offset="<%= Timex.Timezone.total_offset(Timex.Timezone.get(@site.timezone)) %>" data-has-goals="<%= @has_goals %>" data-logged-in="<%= !!@conn.assigns[:current_user] %>" data-inserted-at="<%= @site.inserted_at %>"></div>
<div id="modal_root"></div>
<%= if !@conn.assigns[:current_user] && @conn.assigns[:demo] do %>
<div class="bg-gray-50">
<div class="bg-gray-50 dark:bg-gray-850">
<div class="py-12 lg:py-16 lg:flex lg:items-center lg:justify-between">
<h2 class="text-3xl leading-9 font-extrabold tracking-tight text-gray-900 sm:text-4xl sm:leading-10">
<h2 class="text-3xl leading-9 font-extrabold tracking-tight text-gray-900 sm:text-4xl sm:leading-10 dark:text-gray-100">
Want these stats for your website?
<br />
<span class="text-indigo-600">Start your free trial today.</span>
@ -22,7 +22,7 @@
</a>
</div>
<div class="ml-3 inline-flex rounded-md shadow">
<a href="/" class="inline-flex items-center justify-center px-5 py-3 border border-transparent text-base leading-6 font-medium rounded-md text-indigo-600 bg-white hover:text-indigo-500 focus:outline-none focus:ring transition duration-150 ease-in-out">
<a href="/" class="inline-flex items-center justify-center px-5 py-3 border border-transparent text-base leading-6 font-medium rounded-md text-indigo-600 dark:text-gray-100 bg-white dark:bg-gray-800 hover:text-indigo-500 dark:hover:text-indigo-500 focus:outline-none focus:ring transition duration-150 ease-in-out">
Learn more
</a>
</div>

View File

@ -14,14 +14,14 @@
</script>
<div class="w-full max-w-md mx-auto mt-8">
<div class="bg-white shadow-md rounded px-8 pt-6 pb-8 mb-4 mt-16 relative text-center">
<h2 class="text-xl font-bold">Waiting for first pageview</h2>
<h2 class="text-xl font-bold">on <%= @site.domain %></h2>
<div class="bg-white dark:bg-gray-800 shadow-md rounded px-8 pt-6 pb-8 mb-4 mt-16 relative text-center">
<h2 class="text-xl font-bold dark:text-gray-100">Waiting for first pageview</h2>
<h2 class="text-xl font-bold dark:text-gray-100">on <%= @site.domain %></h2>
<div class="my-44">
<div class="block pulsating-circle"></div>
<p class="text-gray-500 text-xs absolute left-0 bottom-0 mb-6 w-full text-center leading-normal">
<p class="text-gray-600 dark:text-gray-400 text-xs absolute left-0 bottom-0 mb-6 w-full text-center leading-normal">
Need to see the snippet again? <%= link("Click here", to: "/#{URI.encode_www_form(@site.domain)}/snippet")%><br />
Not working? Contact <a href="mailto:support@plausible.io" class="text-gray-700 underline">support@plausible.io</a> to get set up
Not working? Contact <a href="mailto:support@plausible.io" class="text-indigo-500 underline">support@plausible.io</a> to get set up
</p>
</div>
</div>

View File

@ -1,4 +1,4 @@
<div class="w-full max-w-md mx-auto bg-white shadow-md rounded px-8 py-6 mt-8"]>
<h2>Unsubscribe successful</h2>
<p class="mt-4">You will no longer receive a <%= @interval %> analytics report for <%= @site %></p>
<div class="w-full max-w-md mx-auto bg-white dark:bg-gray-800 shadow-md rounded px-8 py-6 mt-8"]>
<h2 class="dark:text-gray-100">Unsubscribe successful</h2>
<p class="mt-4 dark:text-gray-100">You will no longer receive a <%= @interval %> analytics report for <%= @site %></p>
</div>

View File

@ -0,0 +1,9 @@
defmodule Plausible.Repo.Migrations.AddThemePrefToUsers do
use Ecto.Migration
def change do
alter table(:users) do
add_if_not_exists :theme, :string, default: "system"
end
end
end