Merge pull request #412 from plausible/settings-design

Settings design
This commit is contained in:
Uku Taht 2020-11-20 12:56:35 +02:00 committed by GitHub
commit 1ec4dca33f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
33 changed files with 1609 additions and 664 deletions

View File

@ -15,6 +15,7 @@ All notable changes to this project will be documented in this file.
- Display domain's favicon on the home page - Display domain's favicon on the home page
- Ignore consecutive pageviews on same pathname plausible/analytics#417 - Ignore consecutive pageviews on same pathname plausible/analytics#417
- Validate domain format on site creation plausible/analytics#427 - Validate domain format on site creation plausible/analytics#427
- Improve settings UX and design plausible/analytics#412
### Fixed ### Fixed
- Do not error when activating an already activated account plausible/analytics#370 - Do not error when activating an already activated account plausible/analytics#370

View File

@ -7,15 +7,7 @@
@import "tooltip.css"; @import "tooltip.css";
.button { .button {
@apply inline-block bg-indigo-600 text-white text-center font-bold tracking-wide py-2 px-5 rounded no-underline; @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;
}
.button:hover {
@apply shadow;
}
.button:focus {
@apply outline-none;
} }
.button-outline { .button-outline {

View File

@ -3,6 +3,7 @@ import "flatpickr/dist/flatpickr.min.css"
import "./polyfills/closest" import "./polyfills/closest"
import 'abortcontroller-polyfill/dist/polyfill-patch-fetch' import 'abortcontroller-polyfill/dist/polyfill-patch-fetch'
import "phoenix_html" import "phoenix_html"
import 'alpinejs'
const triggers = document.querySelectorAll('[data-dropdown-trigger]') const triggers = document.querySelectorAll('[data-dropdown-trigger]')
@ -33,14 +34,6 @@ if (triggers.length > 0) {
}) })
} }
const flash = document.getElementById('flash')
if (flash) {
setTimeout(function() {
flash.style.display = 'none'
}, 2500)
}
const registerForm = document.getElementById('register-form') const registerForm = document.getElementById('register-form')
if (registerForm) { if (registerForm) {

View File

@ -186,7 +186,7 @@ class DatePicker extends React.Component {
if (this.state.mode === 'menu') { if (this.state.mode === 'menu') {
return ( return (
<div className="absolute mt-2 rounded shadow-md z-10" style={{width: '235px', right: '-14px'}}> <div className="absolute mt-2 rounded shadow-md z-10" style={{width: '235px', right: '-14px'}}>
<div className="rounded bg-white shadow-xs font-medium text-gray-800"> <div className="rounded bg-white ring-1 ring-black ring-opacity-5 font-medium text-gray-800">
<div className="py-1"> <div className="py-1">
{ this.renderLink('day', 'Today') } { this.renderLink('day', 'Today') }
{ this.renderLink('realtime', 'Realtime') } { this.renderLink('realtime', 'Realtime') }

View File

@ -88,7 +88,7 @@ export default class SiteSwitcher extends React.Component {
} }
render() { render() {
const hoverClass = this.props.loggedIn ? 'hover:text-gray-500 focus:border-blue-300 focus:shadow-outline-blue ' : 'cursor-default' const hoverClass = this.props.loggedIn ? 'hover:text-gray-500 focus:border-blue-300 focus:ring ' : 'cursor-default'
return ( return (
<div className="relative inline-block text-left z-10 mr-8"> <div className="relative inline-block text-left z-10 mr-8">
@ -109,7 +109,7 @@ export default class SiteSwitcher extends React.Component {
leaveTo="opacity-0 scale-95" 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="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 shadow-xs"> <div className="rounded-md bg-white ring-1 ring-black ring-opacity-5">
{ this.renderDropdown() } { this.renderDropdown() }
</div> </div>
</div> </div>

View File

@ -106,7 +106,7 @@ class SourcesModal extends React.Component {
} else if (this.state.moreResultsAvailable) { } else if (this.state.moreResultsAvailable) {
return ( return (
<div className="w-full text-center my-4"> <div className="w-full text-center my-4">
<button onClick={this.loadMore.bind(this)} type="button" className="inline-flex items-center px-3 py-2 border border-transparent text-sm leading-4 font-medium rounded-md text-white bg-indigo-600 hover:bg-indigo-500 focus:outline-none focus:border-indigo-700 focus:shadow-outline-indigo active:bg-indigo-700 transition ease-in-out duration-150"> <button onClick={this.loadMore.bind(this)} type="button" className="inline-flex items-center px-3 py-2 border border-transparent text-sm leading-4 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">
Load more Load more
</button> </button>
</div> </div>

1295
assets/package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -9,9 +9,11 @@
"@babel/core": "^7.11.1", "@babel/core": "^7.11.1",
"@babel/preset-env": "^7.11.0", "@babel/preset-env": "^7.11.0",
"@babel/preset-react": "^7.10.4", "@babel/preset-react": "^7.10.4",
"@fullhuman/postcss-purgecss": "^2.3.0", "@tailwindcss/aspect-ratio": "^0.2.0",
"@tailwindcss/ui": "^0.1.4", "@tailwindcss/forms": "^0.2.1",
"@tailwindcss/typography": "^0.3.1",
"abortcontroller-polyfill": "^1.5.0", "abortcontroller-polyfill": "^1.5.0",
"alpinejs": "^2.7.3",
"autoprefixer": "^9.8.6", "autoprefixer": "^9.8.6",
"babel-loader": "^8.1.0", "babel-loader": "^8.1.0",
"chart.js": "^2.9.3", "chart.js": "^2.9.3",
@ -22,17 +24,20 @@
"optimize-css-assets-webpack-plugin": "^5.0.3", "optimize-css-assets-webpack-plugin": "^5.0.3",
"phoenix": "file:../deps/phoenix", "phoenix": "file:../deps/phoenix",
"phoenix_html": "file:../deps/phoenix_html", "phoenix_html": "file:../deps/phoenix_html",
"postcss-loader": "^3.0.0", "postcss": "^7.0.35",
"postcss-loader": "^4.0.4",
"react": "^16.13.1", "react": "^16.13.1",
"react-dom": "^16.13.1", "react-dom": "^16.13.1",
"react-flatpickr": "^3.10.5", "react-flatpickr": "^3.10.5",
"react-flip-move": "^3.0.4", "react-flip-move": "^3.0.4",
"react-router-dom": "^5.2.0", "react-router-dom": "^5.2.0",
"react-transition-group": "^4.4.1", "react-transition-group": "^4.4.1",
"tailwindcss": "^1.6.2", "tailwindcss": "^2.0.1-compat",
"uglifyjs-webpack-plugin": "^2.2.0",
"url-search-params-polyfill": "^8.0.0", "url-search-params-polyfill": "^8.0.0",
"webpack": "4.39.2", "webpack": "4.39.2",
"webpack-cli": "^3.3.12" "webpack-cli": "^3.3.12"
},
"devDependencies": {
"terser-webpack-plugin": "^4.2.3"
} }
} }

View File

@ -1,19 +1,6 @@
const purgecss = require('@fullhuman/postcss-purgecss')({
content: [
'./js/**/*.js',
'../lib/plausible_web/templates/**/*.html.eex',
],
defaultExtractor: content => content.match(/[\w-/.:]+(?<!:)/g) || [],
whitelistPatterns: [/flatpickr\S+/],
whitelistPatternsChildren: [/flatpickr\S+/]
})
module.exports = { module.exports = {
plugins: [ plugins: {
require('tailwindcss'), tailwindcss: {},
require('autoprefixer'), autoprefixer: {},
...process.env.NODE_ENV === 'production' }
? [purgecss]
: []
]
} }

View File

@ -1,10 +1,20 @@
const colors = require('tailwindcss/colors')
module.exports = { module.exports = {
purge: [
'./js/**/*.js',
'../lib/plausible_web/templates/**/*.html.eex',
],
darkMode: false,
theme: { theme: {
container: { container: {
center: true, center: true,
padding: '1rem', padding: '1rem',
}, },
extend: { extend: {
colors: {
orange: colors.orange,
},
spacing: { spacing: {
'44': '11rem' '44': '11rem'
}, },
@ -17,8 +27,9 @@ module.exports = {
textColor: ['responsive', 'hover', 'focus', 'group-hover'], textColor: ['responsive', 'hover', 'focus', 'group-hover'],
display: ['responsive', 'hover', 'focus', 'group-hover'] display: ['responsive', 'hover', 'focus', 'group-hover']
}, },
corePlugins: {},
plugins: [ plugins: [
require('@tailwindcss/ui') require('@tailwindcss/forms'),
], require('@tailwindcss/typography'),
require('@tailwindcss/aspect-ratio'),
]
} }

View File

@ -1,7 +1,7 @@
const path = require('path'); const path = require('path');
const glob = require('glob'); const glob = require('glob');
const MiniCssExtractPlugin = require('mini-css-extract-plugin'); const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const UglifyJsPlugin = require('uglifyjs-webpack-plugin'); const TerserPlugin = require('terser-webpack-plugin');
const OptimizeCSSAssetsPlugin = require('optimize-css-assets-webpack-plugin'); const OptimizeCSSAssetsPlugin = require('optimize-css-assets-webpack-plugin');
const CopyWebpackPlugin = require('copy-webpack-plugin'); const CopyWebpackPlugin = require('copy-webpack-plugin');
const DefinePlugin = require('webpack').DefinePlugin; const DefinePlugin = require('webpack').DefinePlugin;
@ -9,7 +9,7 @@ const DefinePlugin = require('webpack').DefinePlugin;
module.exports = (env, options) => ({ module.exports = (env, options) => ({
optimization: { optimization: {
minimizer: [ minimizer: [
new UglifyJsPlugin({ cache: true, parallel: true, sourceMap: false }), new TerserPlugin(),
new OptimizeCSSAssetsPlugin({}) new OptimizeCSSAssetsPlugin({})
] ]
}, },

View File

@ -65,7 +65,7 @@ defmodule PlausibleWeb.SiteController do
{:ok, _} -> {:ok, _} ->
conn conn
|> put_flash(:success, "Goal created succesfully") |> put_flash(:success, "Goal created succesfully")
|> redirect(to: "/#{URI.encode_www_form(site.domain)}/settings") |> redirect(to: "/#{URI.encode_www_form(site.domain)}/settings/goals")
{:error, :goal, changeset, _} -> {:error, :goal, changeset, _} ->
conn conn
@ -83,35 +83,100 @@ defmodule PlausibleWeb.SiteController do
conn conn
|> put_flash(:success, "Goal deleted succesfully") |> put_flash(:success, "Goal deleted succesfully")
|> redirect(to: "/#{URI.encode_www_form(website)}/settings") |> redirect(to: "/#{URI.encode_www_form(website)}/settings/goals")
end end
def settings(conn, %{"website" => website}) do def settings(conn, %{"website" => website}) do
site = redirect(conn, to: "/#{URI.encode_www_form(website)}/settings/general")
Sites.get_for_user!(conn.assigns[:current_user].id, website) end
def settings_general(conn, %{"website" => website}) do
site = Sites.get_for_user!(conn.assigns[:current_user].id, website)
|> Repo.preload(:custom_domain)
conn
|> assign(:skip_plausible_tracking, true)
|> render("settings_general.html",
site: site,
changeset: Plausible.Site.changeset(site, %{}),
layout: {PlausibleWeb.LayoutView, "site_settings.html"}
)
end
def settings_visibility(conn, %{"website" => website}) do
site = Sites.get_for_user!(conn.assigns[:current_user].id, website)
shared_links = Repo.all(from l in Plausible.Site.SharedLink, where: l.site_id == ^site.id)
conn
|> assign(:skip_plausible_tracking, true)
|> render("settings_visibility.html",
site: site,
shared_links: shared_links,
layout: {PlausibleWeb.LayoutView, "site_settings.html"}
)
end
def settings_goals(conn, %{"website" => website}) do
site = Sites.get_for_user!(conn.assigns[:current_user].id, website)
goals = Goals.for_site(site.domain)
conn
|> assign(:skip_plausible_tracking, true)
|> render("settings_goals.html",
site: site,
goals: goals,
layout: {PlausibleWeb.LayoutView, "site_settings.html"}
)
end
def settings_search_console(conn, %{"website" => website}) do
site = Sites.get_for_user!(conn.assigns[:current_user].id, website)
|> Repo.preload(:google_auth) |> Repo.preload(:google_auth)
|> Repo.preload(:custom_domain)
search_console_domains = search_console_domains =
if site.google_auth do if site.google_auth do
Plausible.Google.Api.fetch_verified_properties(site.google_auth) Plausible.Google.Api.fetch_verified_properties(site.google_auth)
end end
weekly_report = Repo.get_by(Plausible.Site.WeeklyReport, site_id: site.id) conn
monthly_report = Repo.get_by(Plausible.Site.MonthlyReport, site_id: site.id) |> assign(:skip_plausible_tracking, true)
goals = Goals.for_site(site.domain) |> render("settings_search_console.html",
shared_links = Repo.all(from l in Plausible.Site.SharedLink, where: l.site_id == ^site.id) site: site,
search_console_domains: search_console_domains,
layout: {PlausibleWeb.LayoutView, "site_settings.html"}
)
end
def settings_email_reports(conn, %{"website" => website}) do
site = Sites.get_for_user!(conn.assigns[:current_user].id, website)
conn conn
|> assign(:skip_plausible_tracking, true) |> assign(:skip_plausible_tracking, true)
|> render("settings.html", |> render("settings_email_reports.html",
site: site, site: site,
weekly_report: weekly_report, weekly_report: Repo.get_by(Plausible.Site.WeeklyReport, site_id: site.id),
monthly_report: monthly_report, monthly_report: Repo.get_by(Plausible.Site.MonthlyReport, site_id: site.id),
search_console_domains: search_console_domains, layout: {PlausibleWeb.LayoutView, "site_settings.html"}
goals: goals, )
shared_links: shared_links, end
changeset: Plausible.Site.changeset(site, %{})
def settings_custom_domain(conn, %{"website" => website}) do
site = Sites.get_for_user!(conn.assigns[:current_user].id, website)
|> Repo.preload(:custom_domain)
conn
|> assign(:skip_plausible_tracking, true)
|> render("settings_custom_domain.html", site: site, layout: {PlausibleWeb.LayoutView, "site_settings.html"})
end
def settings_danger_zone(conn, %{"website" => website}) do
site = Sites.get_for_user!(conn.assigns[:current_user].id, website)
conn
|> assign(:skip_plausible_tracking, true)
|> render("settings_danger_zone.html",
site: site,
layout: {PlausibleWeb.LayoutView, "site_settings.html"}
) )
end end
@ -125,7 +190,7 @@ defmodule PlausibleWeb.SiteController do
conn conn
|> put_flash(:success, "Google integration saved succesfully") |> put_flash(:success, "Google integration saved succesfully")
|> redirect(to: "/#{URI.encode_www_form(site.domain)}/settings#google-auth") |> redirect(to: "/#{URI.encode_www_form(site.domain)}/settings/search-console")
end end
def delete_google_auth(conn, %{"website" => website}) do def delete_google_auth(conn, %{"website" => website}) do
@ -136,8 +201,8 @@ defmodule PlausibleWeb.SiteController do
Repo.delete!(site.google_auth) Repo.delete!(site.google_auth)
conn conn
|> put_flash(:success, "Google account unlinked succesfully") |> put_flash(:success, "Google account unlinked from Plausible")
|> redirect(to: "/#{URI.encode_www_form(site.domain)}/settings#google-auth") |> redirect(to: "/#{URI.encode_www_form(site.domain)}/settings/search-console")
end end
def update_settings(conn, %{"website" => website, "site" => site_params}) do def update_settings(conn, %{"website" => website, "site" => site_params}) do
@ -151,11 +216,11 @@ defmodule PlausibleWeb.SiteController do
conn conn
|> put_session(site_session_key, nil) |> put_session(site_session_key, nil)
|> put_flash(:success, "Site settings saved succesfully") |> put_flash(:success, "Your site settings have been saved")
|> redirect(to: "/#{URI.encode_www_form(site.domain)}/settings") |> redirect(to: "/#{URI.encode_www_form(site.domain)}/settings/general")
{:error, changeset} -> {:error, changeset} ->
render("settings.html", site: site, changeset: changeset) render("settings_general.html", site: site, changeset: changeset)
end end
end end
@ -165,7 +230,7 @@ defmodule PlausibleWeb.SiteController do
conn conn
|> put_flash(:success, "#{site.domain} stats will be reset in a few minutes") |> put_flash(:success, "#{site.domain} stats will be reset in a few minutes")
|> redirect(to: "/#{URI.encode_www_form(site.domain)}/settings") |> redirect(to: "/#{URI.encode_www_form(site.domain)}/settings/danger-zone")
end end
def delete_site(conn, %{"website" => website}) do def delete_site(conn, %{"website" => website}) do
@ -194,8 +259,8 @@ defmodule PlausibleWeb.SiteController do
|> Repo.update!() |> Repo.update!()
conn conn
|> put_flash(:success, "Congrats! Stats for #{site.domain} are now public.") |> put_flash(:success, "Stats for #{site.domain} are now public.")
|> redirect(to: "/#{URI.encode_www_form(site.domain)}/settings") |> redirect(to: "/#{URI.encode_www_form(site.domain)}/settings/visibility")
end end
def make_private(conn, %{"website" => website}) do def make_private(conn, %{"website" => website}) do
@ -206,7 +271,7 @@ defmodule PlausibleWeb.SiteController do
conn conn
|> put_flash(:success, "Stats for #{site.domain} are now private.") |> put_flash(:success, "Stats for #{site.domain} are now private.")
|> redirect(to: "/#{URI.encode_www_form(site.domain)}/settings") |> redirect(to: "/#{URI.encode_www_form(site.domain)}/settings/visibility")
end end
def enable_weekly_report(conn, %{"website" => website}) do def enable_weekly_report(conn, %{"website" => website}) do
@ -219,8 +284,8 @@ defmodule PlausibleWeb.SiteController do
|> Repo.insert!() |> Repo.insert!()
conn conn
|> put_flash(:success, "Success! You will receive an email report every Monday going forward") |> put_flash(:success, "You will receive an email report every Monday going forward")
|> redirect(to: "/#{URI.encode_www_form(site.domain)}/settings#email-reports") |> redirect(to: "/#{URI.encode_www_form(site.domain)}/settings/email-reports")
end end
def disable_weekly_report(conn, %{"website" => website}) do def disable_weekly_report(conn, %{"website" => website}) do
@ -228,8 +293,8 @@ defmodule PlausibleWeb.SiteController do
Repo.delete_all(from wr in Plausible.Site.WeeklyReport, where: wr.site_id == ^site.id) Repo.delete_all(from wr in Plausible.Site.WeeklyReport, where: wr.site_id == ^site.id)
conn conn
|> put_flash(:success, "Success! You will not receive weekly email reports going forward") |> put_flash(:success, "You will not receive weekly email reports going forward")
|> redirect(to: "/#{URI.encode_www_form(site.domain)}/settings#email-reports") |> redirect(to: "/#{URI.encode_www_form(site.domain)}/settings/email-reports")
end end
def add_weekly_report_recipient(conn, %{"website" => website, "recipient" => recipient}) do def add_weekly_report_recipient(conn, %{"website" => website, "recipient" => recipient}) do
@ -240,8 +305,8 @@ defmodule PlausibleWeb.SiteController do
|> Repo.update!() |> Repo.update!()
conn conn
|> put_flash(:success, "Succesfully added #{recipient} as a recipient for the weekly report") |> put_flash(:success, "Added #{recipient} as a recipient for the weekly report")
|> redirect(to: "/#{URI.encode_www_form(site.domain)}/settings#email-reports") |> redirect(to: "/#{URI.encode_www_form(site.domain)}/settings/email-reports")
end end
def remove_weekly_report_recipient(conn, %{"website" => website, "recipient" => recipient}) do def remove_weekly_report_recipient(conn, %{"website" => website, "recipient" => recipient}) do
@ -254,9 +319,9 @@ defmodule PlausibleWeb.SiteController do
conn conn
|> put_flash( |> put_flash(
:success, :success,
"Succesfully removed #{recipient} as a recipient for the weekly report" "Removed #{recipient} as a recipient for the weekly report"
) )
|> redirect(to: "/#{URI.encode_www_form(site.domain)}/settings#email-reports") |> redirect(to: "/#{URI.encode_www_form(site.domain)}/settings/email-reports")
end end
def enable_monthly_report(conn, %{"website" => website}) do def enable_monthly_report(conn, %{"website" => website}) do
@ -269,8 +334,8 @@ defmodule PlausibleWeb.SiteController do
|> Repo.insert!() |> Repo.insert!()
conn conn
|> put_flash(:success, "Success! You will receive an email report every month going forward") |> put_flash(:success, "You will receive an email report every month going forward")
|> redirect(to: "/#{URI.encode_www_form(site.domain)}/settings#email-reports") |> redirect(to: "/#{URI.encode_www_form(site.domain)}/settings/email-reports")
end end
def disable_monthly_report(conn, %{"website" => website}) do def disable_monthly_report(conn, %{"website" => website}) do
@ -278,8 +343,8 @@ defmodule PlausibleWeb.SiteController do
Repo.delete_all(from mr in Plausible.Site.MonthlyReport, where: mr.site_id == ^site.id) Repo.delete_all(from mr in Plausible.Site.MonthlyReport, where: mr.site_id == ^site.id)
conn conn
|> put_flash(:success, "Success! You will not receive monthly email reports going forward") |> put_flash(:success, "You will not receive monthly email reports going forward")
|> redirect(to: "/#{URI.encode_www_form(site.domain)}/settings#email-reports") |> redirect(to: "/#{URI.encode_www_form(site.domain)}/settings/email-reports")
end end
def add_monthly_report_recipient(conn, %{"website" => website, "recipient" => recipient}) do def add_monthly_report_recipient(conn, %{"website" => website, "recipient" => recipient}) do
@ -290,8 +355,8 @@ defmodule PlausibleWeb.SiteController do
|> Repo.update!() |> Repo.update!()
conn conn
|> put_flash(:success, "Succesfully added #{recipient} as a recipient for the monthly report") |> put_flash(:success, "Added #{recipient} as a recipient for the monthly report")
|> redirect(to: "/#{URI.encode_www_form(site.domain)}/settings#email-reports") |> redirect(to: "/#{URI.encode_www_form(site.domain)}/settings/email-reports")
end end
def remove_monthly_report_recipient(conn, %{"website" => website, "recipient" => recipient}) do def remove_monthly_report_recipient(conn, %{"website" => website, "recipient" => recipient}) do
@ -304,9 +369,9 @@ defmodule PlausibleWeb.SiteController do
conn conn
|> put_flash( |> put_flash(
:success, :success,
"Succesfully removed #{recipient} as a recipient for the monthly report" "Removed #{recipient} as a recipient for the monthly report"
) )
|> redirect(to: "/#{URI.encode_www_form(site.domain)}/settings#email-reports") |> redirect(to: "/#{URI.encode_www_form(site.domain)}/settings/email-reports")
end end
def new_shared_link(conn, %{"website" => website}) do def new_shared_link(conn, %{"website" => website}) do
@ -336,7 +401,7 @@ defmodule PlausibleWeb.SiteController do
case Repo.insert(changes) do case Repo.insert(changes) do
{:ok, _created} -> {:ok, _created} ->
redirect(conn, to: "/#{URI.encode_www_form(site.domain)}/settings#visibility") redirect(conn, to: "/#{URI.encode_www_form(site.domain)}/settings/visibility")
{:error, changeset} -> {:error, changeset} ->
conn conn
@ -355,7 +420,7 @@ defmodule PlausibleWeb.SiteController do
Repo.get_by(Plausible.Site.SharedLink, slug: slug) Repo.get_by(Plausible.Site.SharedLink, slug: slug)
|> Repo.delete!() |> Repo.delete!()
redirect(conn, to: "/#{URI.encode_www_form(site.domain)}/settings#visibility") redirect(conn, to: "/#{URI.encode_www_form(site.domain)}/settings/visibility")
end end
def new_custom_domain(conn, %{"website" => website}) do def new_custom_domain(conn, %{"website" => website}) do
@ -424,7 +489,7 @@ defmodule PlausibleWeb.SiteController do
conn conn
|> put_flash(:success, "Custom domain deleted succesfully") |> put_flash(:success, "Custom domain deleted succesfully")
|> redirect(to: "/#{URI.encode_www_form(site.domain)}/settings") |> redirect(to: "/#{URI.encode_www_form(site.domain)}/settings/custom-domain")
end end
defp insert_site(user_id, params) do defp insert_site(user_id, params) do

View File

@ -145,7 +145,13 @@ defmodule PlausibleWeb.Router do
get "/:website/snippet", SiteController, :add_snippet get "/:website/snippet", SiteController, :add_snippet
get "/:website/settings", SiteController, :settings get "/:website/settings", SiteController, :settings
get "/:website/goals", SiteController, :goals get "/:website/settings/general", SiteController, :settings_general
get "/:website/settings/visibility", SiteController, :settings_visibility
get "/:website/settings/goals", SiteController, :settings_goals
get "/:website/settings/search-console", SiteController, :settings_search_console
get "/:website/settings/email-reports", SiteController, :settings_email_reports
get "/:website/settings/custom-domain", SiteController, :settings_custom_domain
get "/:website/settings/danger-zone", SiteController, :settings_danger_zone
get "/:website/goals/new", SiteController, :new_goal get "/:website/goals/new", SiteController, :new_goal
post "/:website/goals", SiteController, :create_goal post "/:website/goals", SiteController, :create_goal
delete "/:website/goals/:id", SiteController, :delete_goal delete "/:website/goals/:id", SiteController, :delete_goal

View File

@ -80,11 +80,11 @@
<%= cond do %> <%= cond do %>
<% @subscription && @subscription.status in ["active", "past_due", "paused"] && @subscription.cancel_url -> %> <% @subscription && @subscription.status in ["active", "past_due", "paused"] && @subscription.cancel_url -> %>
<div class="mt-8"> <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:shadow-outline-blue 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 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") %>
</div> </div>
<% true -> %> <% true -> %>
<div class="mt-8"> <div class="mt-8">
<%= link("Upgrade", to: "/billing/upgrade", class: "inline-block px-3 py-2 border border-transparent text-sm leading-4 font-medium rounded-md text-white bg-indigo-600 hover:bg-indigo-500 focus:outline-none focus:border-indigo-700 focus:shadow-outline-indigo active:bg-indigo-700 transition ease-in-out duration-150") %> <%= link("Upgrade", to: "/billing/upgrade", class: "inline-block px-3 py-2 border border-transparent text-sm leading-4 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") %>
</div> </div>
<% end %> <% end %>
</div> </div>
@ -123,6 +123,6 @@
<span class="button bg-gray-300 mt-6 hover:shadow-none">Delete my account</span> <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> <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>
<% else %> <% 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:shadow-outline-blue 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 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?"]) %>
<% end %> <% end %>
</div> </div>

View File

@ -20,10 +20,10 @@
<div class="pt-8"></div> <div class="pt-8"></div>
<span class="relative z-0 inline-flex shadow-sm w-full"> <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:shadow-outline-blue 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 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">
Monthly billing Monthly billing
</button> </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:shadow-outline-blue 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 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">
Yearly billing 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-100 text-yellow-800">
-33% -33%
@ -105,7 +105,7 @@
<div class="text-right mt-6"> <div class="text-right mt-6">
<span class="inline-flex rounded-md shadow-sm"> <span class="inline-flex rounded-md shadow-sm">
<a x-show="window.plans[billingCycle][volume].product_id !== '<%= @subscription.paddle_plan_id %>'" style="display: none;" :href="'/billing/change-plan/preview/' + window.plans[billingCycle][volume].product_id" 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:shadow-outline-indigo active:bg-indigo-700 transition ease-in-out duration-150"> <a x-show="window.plans[billingCycle][volume].product_id !== '<%= @subscription.paddle_plan_id %>'" style="display: none;" :href="'/billing/change-plan/preview/' + window.plans[billingCycle][volume].product_id" 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">
<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> <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 Preview changes
</a> </a>
@ -124,5 +124,4 @@
</div> </div>
<script type="text/javascript" src="https://cdn.paddle.com/paddle/paddle.js"></script> <script type="text/javascript" src="https://cdn.paddle.com/paddle/paddle.js"></script>
<script src="https://cdn.jsdelivr.net/gh/alpinejs/alpine@v2.0.1/dist/alpine.js" defer></script>
<script>Paddle.Setup({vendor: 49430})</script> <script>Paddle.Setup({vendor: 49430})</script>

View File

@ -66,12 +66,12 @@
<div class="flex items-center justify-between mt-10"> <div class="flex items-center justify-between mt-10">
<span class="flex rounded-md shadow-sm"> <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:shadow-outline-blue 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 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">
Back Back
</a> </a>
</span> </span>
<span class="flex space-betwee rounded-md shadow-sm"> <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:shadow-outline-indigo active:bg-indigo-700 transition ease-in-out duration-150") %> <%= 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") %>
</span> </span>
</div> </div>
</div> </div>
@ -82,5 +82,4 @@
</div> </div>
<script type="text/javascript" src="https://cdn.paddle.com/paddle/paddle.js"></script> <script type="text/javascript" src="https://cdn.paddle.com/paddle/paddle.js"></script>
<script src="https://cdn.jsdelivr.net/gh/alpinejs/alpine@v2.0.1/dist/alpine.js" defer></script>
<script>Paddle.Setup({vendor: 49430})</script> <script>Paddle.Setup({vendor: 49430})</script>

View File

@ -16,10 +16,10 @@
<div class="pt-2"></div> <div class="pt-2"></div>
<span class="relative z-0 inline-flex shadow-sm w-full"> <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:shadow-outline-blue 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 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">
Monthly billing Monthly billing
</button> </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:shadow-outline-blue 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 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">
Yearly billing 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-100 text-yellow-800">
-33% -33%
@ -102,7 +102,7 @@
<div class="text-right mt-6"> <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">Due today: <b x-text="window.plans[billingCycle][planSize].due_now">$6</b></div>
<span class="inline-flex rounded-md shadow-sm"> <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:shadow-outline-indigo active:bg-indigo-700 transition ease-in-out duration-150"> <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> <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>
Pay securely via Paddle Pay securely via Paddle
</button> </button>
@ -128,5 +128,4 @@
</div> </div>
<script type="text/javascript" src="https://cdn.paddle.com/paddle/paddle.js"></script> <script type="text/javascript" src="https://cdn.paddle.com/paddle/paddle.js"></script>
<script src="https://cdn.jsdelivr.net/gh/alpinejs/alpine@v2.0.1/dist/alpine.js" defer></script>
<script>Paddle.Setup({vendor: 49430})</script> <script>Paddle.Setup({vendor: 49430})</script>

View File

@ -0,0 +1,34 @@
<%= 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 class="rounded-lg ring-1 ring-black ring-opacity-5 overflow-hidden">
<div class="p-4">
<div class="flex items-start">
<div class="flex-shrink-0">
<!-- Heroicon name: check-circle -->
<svg class="h-6 w-6 text-green-400" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z" />
</svg>
</div>
<div class="ml-3 w-0 flex-1 pt-0.5">
<p class="text-sm leading-5 font-medium text-gray-900">
<%= get_flash(@conn, :success_title) || "Success!" %>
</p>
<p class="mt-1 text-sm leading-5 text-gray-500">
<%= 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">
<!-- 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" />
</svg>
</button>
</div>
</div>
</div>
</div>
</div>
</div>
<% end %>

View File

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

View File

@ -0,0 +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>
<% 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>
<% end %>

View File

@ -51,7 +51,7 @@
<ul class="flex" x-show="!document.cookie.includes('logged_in=true')"> <ul class="flex" x-show="!document.cookie.includes('logged_in=true')">
<li> <li>
<div class="inline-flex rounded shadow ml-6"> <div class="inline-flex rounded shadow ml-6">
<a href="/" 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:shadow-outline transition duration-150 ease-in-out">My Sites</a> <a href="/" 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">My Sites</a>
</div> </div>
</li> </li>
</ul> </ul>
@ -70,7 +70,7 @@
<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 hover:text-gray-900 focus:outline-none focus:text-gray-900 transition duration-150 ease-in-out">Login</a>
</div> </div>
<div class="inline-flex rounded shadow ml-6"> <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:shadow-outline transition duration-150 ease-in-out">Sign up</a> <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>
</div> </div>
</li> </li>
</ul> </ul>
@ -80,11 +80,7 @@
</div> </div>
</nav> </nav>
<%= if get_flash(@conn, :success) do %> <%= render("_flash.html", assigns) %>
<div id="flash" class="text-center bg-green-500 text-green-100 text-sm font-bold px-4 absolute w-full transition overflow-hidden" style="top: 91px" role="alert">
<p class="py-3 "><%= get_flash(@conn, :success) %></p>
</div>
<% end %>
<%= if get_flash(@conn, :error) do %> <%= if get_flash(@conn, :error) do %>
<div id="flash" class="text-center bg-red-dark-500 text-red-100 text-sm font-bold px-4 absolute w-full transition overflow-hidden" style="top: 91px" role="alert"> <div id="flash" class="text-center bg-red-dark-500 text-red-100 text-sm font-bold px-4 absolute w-full transition overflow-hidden" style="top: 91px" role="alert">
@ -106,7 +102,7 @@
</div> </div>
<div class="order-3 mt-2 flex-shrink-0 w-full sm:order-2 sm:mt-0 sm:w-auto"> <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"> <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:shadow-outline 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 bg-white hover:text-gray-500 focus:outline-none focus:ring transition ease-in-out duration-150") %>
</div> </div>
</div> </div>
</div> </div>
@ -128,7 +124,7 @@
</div> </div>
<div class="order-3 mt-2 flex-shrink-0 w-full sm:order-2 sm:mt-0 sm:w-auto"> <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"> <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:shadow-outline 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 bg-white hover:text-gray-500 focus:outline-none focus:ring transition ease-in-out duration-150") %>
</div> </div>
</div> </div>
</div> </div>
@ -137,7 +133,7 @@
<% end %> <% end %>
<main class="flex-1"> <main class="flex-1">
<%= render @view_module, @view_template, assigns %> <%= Map.get(assigns, :inner_layout) || render @view_module, @view_template, assigns %>
</main> </main>
<%= render("_footer.html", assigns) %> <%= render("_footer.html", assigns) %>
<script type="text/javascript" src="<%= Routes.static_path(@conn, "/js/app.js") %>"></script> <script type="text/javascript" src="<%= Routes.static_path(@conn, "/js/app.js") %>"></script>

View File

@ -0,0 +1,26 @@
<%= 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">
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) %>
<% end %>
<div class="hidden lg:block">
<%= for [key: key, value: val] <- settings_tabs() do %>
<%= render("_settings_tab.html", this_tab: val, text: key, site: @site, conn: @conn) %>
<% end %>
</div>
</div>
<div class="space-y-6 lg:col-span-9 lg:mt-4">
<%= render @view_module, @view_template, assigns %>
</div>
</div>
</div>
<% end %>

View File

@ -18,7 +18,7 @@
</a> </a>
</div> </div>
</div> </div>
<%= link("Back to settings →", class: "button mt-4 w-full", to: "/#{URI.encode_www_form(@site.domain)}/settings#custom-domain") %> <%= 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"> <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") %> Problems? <%= link("Get help via email", to: "mailto:support@plausible.io", class: "text-indigo-800 underline") %>
</p> </p>

View File

@ -1,258 +0,0 @@
<div class="pt-12"></div>
<div class="max-w-xl mx-auto flex justify-between">
<a href="/<%= URI.encode_www_form(@site.domain) %>"><h1 class="text-2xl font-black">Settings for <%= @site.domain %></h1></a>
</div>
<div class="max-w-xl mx-auto bg-white shadow-md rounded rounded-t-none border-t-2 border-indigo-100 px-8 pt-6 pb-8 mt-6">
<div class="flex items-center justify-between">
<h2 class="text-xl font-black">General</h2>
</div>
<div class="my-4 border-b border-gray-200"></div>
<%= form_for @changeset, "/#{URI.encode_www_form(@site.domain)}/settings", [class: "max-w-xs"], fn f -> %>
<div class="my-4">
<%= label f, :domain, class: "block text-gray-700 text-sm font-bold mb-2" %>
<%= text_input f, :domain, 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:border-gray-300 ", disabled: "disabled" %>
<%= error_tag f, :domain %>
</div>
<div class="my-4">
<%= label f, :timezone, "Reporting Timezone", class: "block text-gray-700 text-sm font-bold mb-2" %>
<div class="inline-block relative w-full">
<%= select f, :timezone, Plausible.Timezones.options(), class: "block appearance-none w-full bg-gray-100 text-gray-700 cursor-pointer hover:border-gray-500 p-2 pr-8 rounded leading-normal focus:outline-none" %>
<div class="pointer-events-none absolute inset-y-0 right-0 flex items-center px-2 text-pink-500">
<svg class="fill-current h-4 w-4" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20"><path d="M9.293 12.95l.707.707L15.657 8l-1.414-1.414L10 10.828 5.757 6.586 4.343 8z"/></svg>
</div>
</div>
</div>
<%= submit "Save changes", class: "button mt-4" %>
<% end %>
</div>
<div class="max-w-xl mx-auto bg-white shadow-md rounded rounded-t-none border-t-2 border-indigo-100 px-8 pt-6 pb-8 mt-10" id="visibility">
<h2 class="text-xl font-black">Visibility</h2>
<div class="my-4 border-b border-gray-300"></div>
<%= if @site.public do %>
Stats for <%= @site.domain %> are currently <b>public</b>. Anyone with the following link can view the stats:
<div class="relative text-sm mt-4">
<input type="text" id="public-link" value="<%= plausible_url() <> "/" <> URI.encode_www_form(@site.domain)%>" class="transition bg-gray-100 appearance-none border border-transparent rounded w-full p-2 pr-16 text-gray-700 appearance-none focus:outline-none" />
<a onclick="var input = document.getElementById('public-link'); input.focus(); input.select(); document.execCommand('copy');" href="javascript:void(0)" class="absolute right-0 text-indigo-700 font-bold p-2">
<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>
</a>
</div>
<%= button("Make stats private", to: "/sites/#{URI.encode_www_form(@site.domain)}/make-private", method: "POST", class: "button mt-8") %>
<% else %>
<div class="text-gray-700">
Stats for <%= @site.domain %> are currently <b>private</b>. You are the only person who can see them.
If you choose to make your stats public, anyone with the link will be able to view them.
</div>
<%= button("Make stats public", to: "/sites/#{URI.encode_www_form(@site.domain)}/make-public", method: "POST", class: "button mt-8") %>
<% end %>
<div class="mt-12">
<h2 class="text-xl font-black">Shared links</h2>
<div class="my-4 border-b border-gray-300"></div>
<div class="my-4">
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.
</div>
<%= for link <- @shared_links do %>
<div class="flex relative w-full 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">
<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 %>
<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>
<% end %>
</div>
<%= link("+ New link", to: "/sites/#{URI.encode_www_form(@site.domain)}/shared-links/new", class: "button mt-4") %>
</div>
<div class="max-w-xl mx-auto bg-white shadow-md rounded rounded-t-none border-t-2 border-indigo-100 px-8 pt-6 pb-8 mt-10">
<h2 class="text-xl font-black">Goals</h2>
<div class="my-4 border-b border-gray-300"></div>
<div>
<%= if Enum.count(@goals) > 0 do %>
<%= for goal <- @goals do %>
<div class="border-b border-gray-300 py-3 flex justify-between">
<small class="font-bold"><%= goal_name(goal) %></small>
<%= button("", to: "/#{URI.encode_www_form(@site.domain)}/goals/#{goal.id}", method: :delete, class: "text-sm", 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."]) %>
</div>
<% end %>
<% else %>
<div>No goals configured for this site yet</div>
<% end %>
</div>
<%= link("+ Add goal", to: "/#{URI.encode_www_form(@site.domain)}/goals/new", class: "button mt-6") %>
</div>
<div class="max-w-xl mx-auto bg-white shadow-md rounded rounded-t-none border-t-2 border-indigo-100 px-8 pt-6 pb-8 mt-10" id="google-auth">
<h2 class="text-xl font-black">Google Integration</h2>
<div class="my-4 border-b border-gray-300"></div>
<div class="text-gray-700 my-4">
Integrating with your Google account allows Plausible to show more information about your websites' performance on their search engine.
</div>
<%= 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>
<%= 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:shadow-outline-blue 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">
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">
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 %>
<%= form_for Plausible.Site.GoogleAuth.changeset(@site.google_auth), "/#{URI.encode_www_form(@site.domain)}/settings/google", [class: "max-w-xs"], fn f -> %>
<div class="my-6">
<div class="inline-block relative w-full">
<%= select f, :property, domains, prompt: "(Choose property)", class: "block appearance-none w-full bg-gray-100 text-gray-700 cursor-pointer hover:border-gray-500 p-2 pr-8 rounded leading-normal focus:outline-none" %>
<div class="pointer-events-none absolute inset-y-0 right-0 flex items-center px-2 text-pink-500">
<svg class="fill-current h-4 w-4" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20"><path d="M9.293 12.95l.707.707L15.657 8l-1.414-1.414L10 10.828 5.757 6.586 4.343 8z"/></svg>
</div>
</div>
</div>
<%= 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-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-4") %>
<div class="text-gray-700 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 %>
</div>
<div class="max-w-xl mx-auto bg-white shadow-md rounded rounded-t-none border-t-2 border-indigo-100 px-8 pt-6 pb-8 mt-10" id="email-reports">
<h2 class="text-xl font-black">Email reports</h2>
<div class="my-4 border-b border-gray-300"></div>
<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: "border rounded-full border-gray-300 flex items-center cursor-pointer w-8 bg-green-500 justify-end") do %>
<span class="rounded-full border w-4 h-4 border-gray-300 shadow-inner bg-white shadow"></span>
<% end %>
<% else %>
<%= button(to: "/sites/#{URI.encode_www_form(@site.domain)}/weekly-report/enable", method: :post, class: "border rounded-full border-gray-300 flex items-center cursor-pointer w-8 justify-start") do %>
<span class="rounded-full border w-4 h-4 border-gray-300 shadow-inner bg-white shadow"></span>
<% end %>
<% end %>
<span class="ml-2">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>
<%= for recipient <- @weekly_report.recipients do %>
<div class="p-2 flex justify-between bg-gray-100 rounded my-2 max-w-md">
<span>
<svg class="feather mr-1" 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"><path d="M4 4h16c1.1 0 2 .9 2 2v12c0 1.1-.9 2-2 2H4c-1.1 0-2-.9-2-2V6c0-1.1.9-2 2-2z"></path><polyline points="22,6 12,13 2,6"></polyline></svg><%= recipient %>
</span>
<%= button("", to: "/sites/#{URI.encode_www_form(@site.domain)}/weekly-report/recipients/#{recipient}", method: :delete) %>
</div>
<% end %>
<%= form_for @conn, "/sites/#{URI.encode_www_form(@site.domain)}/weekly-report/recipients", fn f -> %>
<div class="flex justify-between my-2 max-w-md">
<%= email_input f, :recipient, 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:border-gray-300", style: "flex-grow: 2", placeholder: "recipient@example.com" %>
<%= submit "Add recipient", class: "button rounded-l-none whitespace-no-wrap" %>
</div>
<% end %>
</div>
<% end %>
<div class="my-8 border-b border-gray-300"></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: "border rounded-full border-gray-300 flex items-center cursor-pointer w-8 bg-green-500 justify-end") do %>
<span class="rounded-full border w-4 h-4 border-gray-300 shadow-inner bg-white shadow"></span>
<% end %>
<% else %>
<%= button(to: "/sites/#{URI.encode_www_form(@site.domain)}/monthly-report/enable", method: :post, class: "border rounded-full border-gray-300 flex items-center cursor-pointer w-8 justify-start") do %>
<span class="rounded-full border w-4 h-4 border-gray-300 shadow-inner bg-white shadow"></span>
<% end %>
<% end %>
<span class="ml-2">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">
<h4 class="font-bold my-2">Monthly report recipients</h4>
<%= for recipient <- @monthly_report.recipients do %>
<div class="p-2 flex justify-between bg-gray-100 rounded my-2 max-w-md">
<span>
<svg class="feather mr-1" 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"><path d="M4 4h16c1.1 0 2 .9 2 2v12c0 1.1-.9 2-2 2H4c-1.1 0-2-.9-2-2V6c0-1.1.9-2 2-2z"></path><polyline points="22,6 12,13 2,6"></polyline></svg><%= recipient %>
</span>
<%= button("", to: "/sites/#{URI.encode_www_form(@site.domain)}/monthly-report/recipients/#{recipient}", method: :delete) %>
</div>
<% end %>
<%= form_for @conn, "/sites/#{URI.encode_www_form(@site.domain)}/monthly-report/recipients", fn f -> %>
<div class="flex justify-between my-2 max-w-md">
<%= email_input f, :recipient, 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:border-gray-300", style: "flex-grow: 2", placeholder: "recipient@example.com" %>
<%= submit "Add recipient", class: "button rounded-l-none whitespace-no-wrap" %>
</div>
<% end %>
</div>
<% end %>
</div>
<div class="bg-white max-w-xl mx-auto shadow-md rounded rounded-t-none border-t-2 border-indigo-100 px-8 pt-6 pb-8 mb-4 mt-16" id="custom-domain">
<h2 class="text-xl font-black">Custom domain</h2>
<div class="my-4">
You can serve the tracking script from your domain name as a first-party resource instead of loading the script from <code>plausible.io</code>.
</div>
<div class="mt-6">
<%= 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:shadow-outline-blue 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 %>
</div>
</div>
<%= form_for @conn, "/", [class: "bg-white max-w-xl mx-auto shadow-md rounded rounded-t-none border-t-2 border-indigo-100 px-8 pt-6 pb-8 mb-4 mt-16"], fn f -> %>
<h2 class="text-xl font-black">Javascript snippet</h2>
<div class="my-4">
<p>Include this snippet in the <code>&lt;head&gt;</code> of your website.</p>
<div class="relative">
<%= textarea f, :domain, id: "snippet_code", class: "transition overflow-hidden bg-gray-100 appearance-none border border-transparent rounded w-full p-2 pr-6 text-gray-700 leading-normal appearance-none focus:outline-none focus:bg-white focus:border-gray-300 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>
</div>
</div>
<% end %>
<div class="max-w-xl 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">
<h2 class="text-xl font-black">Danger zone</h2>
<div class="my-4 border-b border-gray-300"></div>
<p class="text-gray-700">Resetting the stats removes all pageviews but keeps the site configuration</p>
<%= 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 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:shadow-outline-blue 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?"]) %>
<div class="mt-6">
<p class="text-gray-700">Deleting the site removes all stats along with the site configuration</p>
<%= link "Delete #{@site.domain}", to: "/#{URI.encode_www_form(@site.domain)}", method: :delete, class: "inline-block mt-4 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:shadow-outline-red 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?"] %>
</div>
</div>

View File

@ -0,0 +1,18 @@
<div class="shadow bg-white 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>
<%= 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">
<%= 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") %>
<% else %>
<%= link("Add custom domain", to: "/sites/#{URI.encode_www_form(@site.domain)}/custom-domains/new", class: "button") %>
<% end %>
</div>
</div>

View File

@ -0,0 +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>
<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>
</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">
Reset stats
</p>
<p class="text-sm leading-5 text-gray-500">
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?"]) %>
</li>
<div class="border-b border-gray-200"></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">
Delete site
</p>
<p class="text-sm leading-5 text-gray-500">
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?"] %>
</li>
</div>
</div>

View File

@ -0,0 +1,111 @@
<div class="shadow bg-white 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>
<%= 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 %>
</header>
<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>
<% 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>
<% end %>
<% end %>
<span class="ml-2">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>
<%= for recipient <- @weekly_report.recipients do %>
<div class="p-2 pl-3 flex justify-between bg-gray-100 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" />
<path d="M18 8.118l-8 4-8-4V14a2 2 0 002 2h12a2 2 0 002-2V8.118z" />
</svg><%= recipient %>
</span>
<%= button(to: "/sites/#{URI.encode_www_form(@site.domain)}/weekly-report/recipients/#{recipient}", method: :delete) do %>
<svg class="w-4 h-4 text-red-600" 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="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16"></path></svg>
<% end %>
</div>
<% end %>
<%= form_for @conn, "/sites/#{URI.encode_www_form(@site.domain)}/weekly-report/recipients", fn f -> %>
<div class="max-w-md mt-4">
<div class="mt-1 flex rounded-md shadow-sm">
<div class="relative flex items-stretch flex-grow focus-within:z-10">
<div class="absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none">
<svg class="h-5 w-5 text-gray-400" 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" />
<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" %>
</div>
<%= submit class: "-ml-px relative button rounded-l-none" do %>
<svg class="w-5 h-5 mr-1" fill="currentColor" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg"><path d="M8 9a3 3 0 100-6 3 3 0 000 6zM8 11a6 6 0 016 6H2a6 6 0 016-6zM16 7a1 1 0 10-2 0v1h-1a1 1 0 100 2h1v1a1 1 0 102 0v-1h1a1 1 0 100-2h-1V7z"></path></svg>
<span>Add recipient</span>
<% end %>
</div>
</div>
<% end %>
</div>
<% end %>
<div class="my-8 border-b border-gray-300"></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>
<% 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>
<% end %>
<% end %>
<span class="ml-2">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">
<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">
<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" />
<path d="M18 8.118l-8 4-8-4V14a2 2 0 002 2h12a2 2 0 002-2V8.118z" />
</svg><%= recipient %>
</span>
<%= button(to: "/sites/#{URI.encode_www_form(@site.domain)}/monthly-report/recipients/#{recipient}", method: :delete) do %>
<svg class="w-4 h-4 text-red-600" 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="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16"></path></svg>
<% end %>
</div>
<% end %>
<%= form_for @conn, "/sites/#{URI.encode_www_form(@site.domain)}/monthly-report/recipients", fn f -> %>
<div class="max-w-md mt-4">
<div class="mt-1 flex rounded-md shadow-sm">
<div class="relative flex items-stretch flex-grow focus-within:z-10">
<div class="absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none">
<svg class="h-5 w-5 text-gray-400" 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" />
<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" %>
</div>
<%= submit class: "-ml-px relative button rounded-l-none" do %>
<svg class="w-5 h-5 mr-1" fill="currentColor" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg"><path d="M8 9a3 3 0 100-6 3 3 0 000 6zM8 11a6 6 0 016 6H2a6 6 0 016-6zM16 7a1 1 0 10-2 0v1h-1a1 1 0 100 2h1v1a1 1 0 102 0v-1h1a1 1 0 100-2h-1V7z"></path></svg>
<span>Add recipient</span>
<% end %>
</div>
</div>
<% end %>
</div>
<% end %>
</div>

View File

@ -0,0 +1,49 @@
<%= 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">
<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>
<%= 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>
<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" %>
</div>
</div>
</div>
<div class="px-4 py-3 bg-gray-50 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" %>
</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 -> %>
<header class="relative">
<h2 class="text-lg leading-6 font-medium text-gray-900">Javascript snippet</h2>
<p class="mt-1 text-sm leading-5 text-gray-500">Include this snippet in the <code>&lt;head&gt;</code> of your website.</p>
<%= 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>
<% end %>
</header>
<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 %>
<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>
<% end %>

View File

@ -0,0 +1,26 @@
<div class="shadow bg-white 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>
<%= 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 %>
</header>
<%= 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>
<%= 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 %>
</div>
<% end %>
</div>
<% else %>
<div class="mt-4">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") %>
</div>

View File

@ -0,0 +1,48 @@
<div class="shadow bg-white 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>
<%= 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 %>
</header>
<%= 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>
<%= 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") %>
<%= 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">
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">
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 %>
<%= form_for Plausible.Site.GoogleAuth.changeset(@site.google_auth), "/#{URI.encode_www_form(@site.domain)}/settings/google", [class: "max-w-xs"], fn f -> %>
<div class="my-6">
<div class="inline-block relative w-full">
<%= select f, :property, domains, prompt: "(Choose property)", 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" %>
</div>
</div>
<%= 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-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">
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 %>
</div>

View File

@ -0,0 +1,52 @@
<div class="shadow bg-white 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>
<%= 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 %>
</header>
<%= 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>
<% 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>
</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>
<% 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>
</div>
<% end %>
</div>
<div class="shadow bg-white 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>
<%= 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 %>
</header>
<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">
<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 %>
<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>
<% end %>
<%= link("+ New link", to: "/sites/#{URI.encode_www_form(@site.domain)}/shared-links/new", class: "button mt-4") %>
</div>
</div>

View File

@ -17,12 +17,12 @@
</h2> </h2>
<div class="mt-8 flex lg:flex-shrink-0 lg:mt-0"> <div class="mt-8 flex lg:flex-shrink-0 lg:mt-0">
<div class="inline-flex rounded-md shadow"> <div class="inline-flex rounded-md shadow">
<a href="/register" class="inline-flex items-center justify-center px-5 py-3 border border-transparent text-base leading-6 font-medium rounded-md text-white bg-indigo-600 hover:bg-indigo-500 focus:outline-none focus:shadow-outline transition duration-150 ease-in-out"> <a href="/register" class="inline-flex items-center justify-center px-5 py-3 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">
Get started Get started
</a> </a>
</div> </div>
<div class="ml-3 inline-flex rounded-md shadow"> <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:shadow-outline 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 bg-white hover:text-indigo-500 focus:outline-none focus:ring transition duration-150 ease-in-out">
Learn more Learn more
</a> </a>
</div> </div>

View File

@ -21,6 +21,19 @@ defmodule PlausibleWeb.LayoutView do
end end
end end
def settings_tabs() do
[
[key: "General", value: "general"],
[key: "Visibility", value: "visibility"],
[key: "Goals", value: "goals"],
[key: "Search Console", value: "search-console"],
[key: "Email reports", value: "email-reports"],
[key: "Custom domain", value: "custom-domain"],
[key: "Danger zone", value: "danger-zone"],
]
end
def trial_notificaton(user) do def trial_notificaton(user) do
case Plausible.Billing.trial_days_left(user) do case Plausible.Billing.trial_days_left(user) do
days when days > 1 -> days when days > 1 ->
@ -36,4 +49,13 @@ defmodule PlausibleWeb.LayoutView do
"Trial over, upgrade now" "Trial over, upgrade now"
end end
end end
@doc "http://blog.plataformatec.com.br/2018/05/nested-layouts-with-phoenix/"
def render_layout(layout, assigns, do: content) do
render(layout, Map.put(assigns, :inner_layout, content))
end
def is_current_tab(conn, tab) do
List.last(conn.path_info) == tab
end
end end