mirror of
https://github.com/plausible/analytics.git
synced 2024-12-23 17:44:43 +03:00
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:
parent
a50fd555cf
commit
425975efec
@ -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
|
||||
|
@ -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);
|
||||
}
|
||||
|
110
assets/css/flatpickr.dark.css
Normal file
110
assets/css/flatpickr.dark.css
Normal 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;
|
||||
}
|
@ -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;
|
||||
|
@ -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>
|
||||
|
@ -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>
|
||||
|
@ -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>
|
||||
)
|
||||
|
@ -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))
|
||||
|
@ -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>
|
||||
|
@ -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>
|
||||
|
@ -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>
|
||||
|
@ -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))
|
||||
|
@ -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>
|
||||
|
@ -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>
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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') }
|
||||
|
@ -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>
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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>
|
||||
|
@ -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>
|
||||
|
@ -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>
|
||||
|
||||
|
@ -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>
|
||||
|
@ -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>
|
||||
|
@ -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>
|
||||
|
@ -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>
|
||||
|
@ -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() }
|
||||
|
@ -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() }
|
||||
|
@ -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() }
|
||||
|
@ -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>
|
||||
|
@ -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'}>↑</span> {formattedComparison}%</span>
|
||||
return <span className="text-xs dark:text-gray-100"><span className={color + ' font-bold'}>↑</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'}>↓</span> {formattedComparison}%</span>
|
||||
return <span className="text-xs dark:text-gray-100"><span className={color + ' font-bold'}>↓</span> {formattedComparison}%</span>
|
||||
} else if (comparison === 0) {
|
||||
return <span className="text-xs text-gray-700">〰 N/A</span>
|
||||
return <span className="text-xs text-gray-700 dark:text-gray-300">〰 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>
|
||||
|
16
assets/js/dashboard/theme-consumer-hoc.js
Normal file
16
assets/js/dashboard/theme-consumer-hoc.js
Normal 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>
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
5
assets/js/dashboard/theme-context.js
Normal file
5
assets/js/dashboard/theme-context.js
Normal file
@ -0,0 +1,5 @@
|
||||
import React from 'react';
|
||||
|
||||
export const ThemeContext = React.createContext({
|
||||
dark: false
|
||||
});
|
37
assets/js/dashboard/theme-provider-hoc.js
Normal file
37
assets/js/dashboard/theme-provider-hoc.js
Normal 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>
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
BIN
assets/static/images/icon/plausible_logo_100px_dark.png
Normal file
BIN
assets/static/images/icon/plausible_logo_100px_dark.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 12 KiB |
BIN
assets/static/images/icon/plausible_logo_300px_dark.png
Normal file
BIN
assets/static/images/icon/plausible_logo_300px_dark.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 34 KiB |
BIN
assets/static/images/icon/plausible_logo_dark.png
Normal file
BIN
assets/static/images/icon/plausible_logo_dark.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 13 KiB |
37
assets/static/js/applyTheme.js
Normal file
37
assets/static/js/applyTheme.js
Normal 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();
|
||||
};
|
@ -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'),
|
||||
|
@ -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
11
lib/plausible/themes.ex
Normal 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
|
@ -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
|
||||
|
||||
|
@ -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 %>
|
||||
|
@ -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 →</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>
|
||||
|
||||
|
@ -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 %>
|
||||
|
@ -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 %>
|
||||
|
@ -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 %>
|
||||
|
@ -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>
|
||||
|
@ -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>
|
||||
|
@ -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">
|
||||
|
@ -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 →</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 %>
|
||||
|
||||
|
@ -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>
|
||||
|
@ -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>
|
||||
|
||||
|
@ -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>
|
||||
|
||||
|
@ -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>
|
||||
|
||||
|
@ -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>
|
||||
|
@ -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>
|
||||
|
@ -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" />
|
||||
|
@ -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">
|
||||
|
@ -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 %>
|
||||
|
@ -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>
|
||||
|
@ -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>
|
||||
|
||||
|
@ -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 %>
|
||||
|
@ -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>
|
||||
|
@ -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 provider’s 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 provider’s 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">→</span>
|
||||
<span class="text-lg font-bold dark:text-gray-100">→</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>
|
||||
|
@ -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 %>
|
||||
|
@ -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 %>
|
||||
|
@ -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>
|
||||
|
@ -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 %>
|
||||
|
@ -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>
|
||||
|
@ -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>
|
||||
|
||||
|
@ -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 %>
|
||||
|
@ -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>
|
||||
|
@ -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 %>
|
||||
|
@ -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><head></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><head></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>
|
||||
|
@ -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") %>
|
||||
|
@ -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 %>
|
||||
|
@ -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>
|
||||
|
@ -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><head></code> of your website.</p>
|
||||
<p class="dark:text-gray-100">Paste this snippet in the <code><head></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>
|
||||
|
@ -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>
|
||||
|
@ -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>
|
||||
|
@ -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>
|
||||
|
@ -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>
|
||||
|
@ -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
|
Loading…
Reference in New Issue
Block a user