refactor(wip): redesign settings page

This commit is contained in:
Ferdinand Mütsch 2021-12-16 12:39:41 +01:00
parent 4e7322c985
commit 7b7fa8bdf3
15 changed files with 695 additions and 654 deletions

View File

@ -6,7 +6,7 @@ import (
var securityHeaders = map[string]string{
"Cross-Origin-Opener-Policy": "same-origin",
"Content-Security-Policy": "default-src 'self' 'unsafe-inline'; img-src 'self' https: data:; form-action 'self'; block-all-mixed-content;",
"Content-Security-Policy": "default-src 'self' 'unsafe-inline' 'unsafe-eval'; img-src 'self' https: data:; form-action 'self'; block-all-mixed-content;",
"X-Frame-Options": "DENY",
"X-Content-Type-Options": "nosniff",
}

View File

@ -49,18 +49,6 @@ type SummaryItemContainer struct {
Items []*SummaryItem
}
type SummaryViewModel struct {
*Summary
*SummaryParams
User *User
AvatarURL string
LanguageColors map[string]string
Error string
Success string
ApiKey string
RawQuery string
}
type SummaryParams struct {
From time.Time
To time.Time

View File

@ -8,6 +8,7 @@ type SettingsViewModel struct {
Aliases []*SettingsVMCombinedAlias
Labels []*SettingsVMCombinedLabel
Projects []string
ApiKey string
Success string
Error string
}

View File

@ -1,8 +1,17 @@
package view
import "github.com/muety/wakapi/models"
type SummaryViewModel struct {
Success string
*models.Summary
*models.SummaryParams
User *models.User
AvatarURL string
LanguageColors map[string]string
Error string
Success string
ApiKey string
RawQuery string
}
func (s *SummaryViewModel) WithSuccess(m string) *SummaryViewModel {

View File

@ -682,6 +682,7 @@ func (h *SettingsHandler) buildViewModel(r *http.Request) *view.SettingsViewMode
Aliases: combinedAliases,
Labels: combinedLabels,
Projects: projects,
ApiKey: user.ApiKey,
Success: r.URL.Query().Get("success"),
Error: r.URL.Query().Get("error"),
}

View File

@ -4,7 +4,6 @@ import (
"github.com/gorilla/mux"
conf "github.com/muety/wakapi/config"
"github.com/muety/wakapi/middlewares"
"github.com/muety/wakapi/models"
"github.com/muety/wakapi/models/view"
su "github.com/muety/wakapi/routes/utils"
"github.com/muety/wakapi/services"
@ -61,7 +60,7 @@ func (h *SummaryHandler) GetIndex(w http.ResponseWriter, r *http.Request) {
return
}
vm := models.SummaryViewModel{
vm := view.SummaryViewModel{
Summary: summary,
SummaryParams: summaryParams,
User: user,

View File

@ -20,3 +20,4 @@ Iconify.addCollection({"prefix":"fa-solid","icons":{"external-link-alt":{"body":
Iconify.addCollection({"prefix":"simple-icons","icons":{"wakatime":{"body":"<path d=\"M12 0C5.373 0 0 5.373 0 12s5.373 12 12 12s12-5.373 12-12S18.627 0 12 0zm0 2.824a9.176 9.176 0 1 1 0 18.352a9.176 9.176 0 0 1 0-18.352zm5.097 5.058c-.327 0-.61.19-.764.45c-1.025 1.463-2.21 3.162-3.288 4.706l-.387-.636a.897.897 0 0 0-.759-.439a.901.901 0 0 0-.788.492l-.357.581l-1.992-2.943a.897.897 0 0 0-.761-.446c-.514 0-.903.452-.903.96a1 1 0 0 0 .207.61l2.719 3.96c.152.272.44.47.776.47a.91.91 0 0 0 .787-.483c.046-.071.23-.368.314-.504l.324.52c-.035-.047.076.113.087.13c.024.031.054.059.078.085c.019.019.04.036.058.052c.036.033.08.056.115.08c.025.016.052.028.076.04c.029.015.06.024.088.035c.058.025.122.027.18.04c.031.004.064.003.092.005c.29 0 .546-.149.707-.36c1.4-2 2.842-4.055 4.099-5.849A.995.995 0 0 0 18 8.842c0-.508-.389-.96-.903-.96\" fill=\"currentColor\"/>"}},"width":24,"height":24});
Iconify.addCollection({"prefix":"heroicons-solid","icons":{"light-bulb":{"body":"<g fill=\"none\"><path d=\"M11 3a1 1 0 1 0-2 0v1a1 1 0 1 0 2 0V3z\" fill=\"currentColor\"/><path d=\"M15.657 5.757a1 1 0 0 0-1.414-1.414l-.707.707a1 1 0 0 0 1.414 1.414l.707-.707z\" fill=\"currentColor\"/><path d=\"M18 10a1 1 0 0 1-1 1h-1a1 1 0 1 1 0-2h1a1 1 0 0 1 1 1z\" fill=\"currentColor\"/><path d=\"M5.05 6.464A1 1 0 1 0 6.464 5.05l-.707-.707a1 1 0 0 0-1.414 1.414l.707.707z\" fill=\"currentColor\"/><path d=\"M5 10a1 1 0 0 1-1 1H3a1 1 0 1 1 0-2h1a1 1 0 0 1 1 1z\" fill=\"currentColor\"/><path d=\"M8 16v-1h4v1a2 2 0 1 1-4 0z\" fill=\"currentColor\"/><path d=\"M12 14c.015-.34.208-.646.477-.859a4 4 0 1 0-4.954 0c.27.213.462.519.476.859h4.002z\" fill=\"currentColor\"/></g>"},"server":{"body":"<g fill=\"none\"><path fill-rule=\"evenodd\" clip-rule=\"evenodd\" d=\"M2 5a2 2 0 0 1 2-2h12a2 2 0 0 1 2 2v2a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V5zm14 1a1 1 0 1 1-2 0a1 1 0 0 1 2 0z\" fill=\"currentColor\"/><path fill-rule=\"evenodd\" clip-rule=\"evenodd\" d=\"M2 13a2 2 0 0 1 2-2h12a2 2 0 0 1 2 2v2a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2v-2zm14 1a1 1 0 1 1-2 0a1 1 0 0 1 2 0z\" fill=\"currentColor\"/></g>"}},"width":20,"height":20});
Iconify.addCollection({"prefix":"ion","icons":{"rocket":{"body":"<path d=\"M328.85 156.79a26.69 26.69 0 1 0 18.88 7.81a26.6 26.6 0 0 0-18.88-7.81z\" fill=\"currentColor\"/><path d=\"M477.44 50.06a.29.29 0 0 1 0-.09a20.4 20.4 0 0 0-15.13-15.3c-29.8-7.27-76.68.48-128.63 21.28c-52.36 21-101.42 52-134.58 85.22A320.7 320.7 0 0 0 169.55 175c-22.33-1-42 2.18-58.57 9.41c-57.74 25.41-74.23 90.44-78.62 117.14a25 25 0 0 0 27.19 29h.13l64.32-7.02c.08.82.17 1.57.24 2.26a34.36 34.36 0 0 0 9.9 20.72l31.39 31.41a34.27 34.27 0 0 0 20.71 9.91l2.15.23l-7 64.24v.13A25 25 0 0 0 206 480a25.25 25.25 0 0 0 4.15-.34C237 475.34 302 459.05 327.34 401c7.17-16.46 10.34-36.05 9.45-58.34a314.78 314.78 0 0 0 33.95-29.55c33.43-33.26 64.53-81.92 85.31-133.52c20.69-51.36 28.48-98.59 21.39-129.53zM370.38 224.94a58.77 58.77 0 1 1 0-83.07a58.3 58.3 0 0 1 0 83.07z\" fill=\"currentColor\"/><path d=\"M161.93 386.44a16 16 0 0 0-11 2.67c-6.39 4.37-12.81 8.69-19.29 12.9c-13.11 8.52-28.79-6.44-21-20l12.15-21a16 16 0 0 0-15.16-24.91A61.25 61.25 0 0 0 72 353.56c-3.66 3.67-14.79 14.81-20.78 57.26A357.94 357.94 0 0 0 48 447.59A16 16 0 0 0 64 464h.4a359.87 359.87 0 0 0 36.8-3.2c42.47-6 53.61-17.14 57.27-20.8a60.49 60.49 0 0 0 17.39-35.74a16 16 0 0 0-13.93-17.82z\" fill=\"currentColor\"/>"}},"width":512,"height":512});
Iconify.addCollection({"prefix":"clarity","icons":{"warning-standard-solid":{"body":"<path class=\"clr-i-solid clr-i-solid-path-1\" d=\"M34.6 29.21L20.71 3.65a3.22 3.22 0 0 0-5.66 0L1.17 29.21A3.22 3.22 0 0 0 4 34h27.77a3.22 3.22 0 0 0 2.83-4.75zM16.6 10a1.4 1.4 0 0 1 2.8 0v12a1.4 1.4 0 0 1-2.8 0zM18 29.85a1.8 1.8 0 1 1 1.8-1.8a1.8 1.8 0 0 1-1.8 1.8z\" fill=\"currentColor\"/>"}},"width":36,"height":36});

File diff suppressed because one or more lines are too long

View File

@ -1,3 +1,4 @@
<script src="assets/app.js"></script>
<script src="assets/vendor/iconify.basic.min.js"></script>
<script src="assets/vendor/seedrandom.min.js"></script>
<script src="assets/vendor/chart.min.js"></script>

View File

@ -17,4 +17,5 @@
<link href="assets/vendor/tailwind.dist.css" rel="stylesheet">
{{ end }}
<link href="assets/app.css" rel="stylesheet">
<script src="assets/vendor/petite-vue.min.js" defer></script>
</head>

View File

@ -12,7 +12,7 @@
<main class="mt-10 flex-grow flex justify-center w-full">
<div class="flex-grow max-w-lg mt-10">
<div class="mb-8">
<h1 class="font-semibold text-2xl text-white m-0">Welcome!</h1>
<h1 class="font-semibold text-3xl text-white m-0">Welcome!</h1>
<span class="text-gray-600">Log in to continue using Wakapi</span>
</div>
<form action="login" method="post">

View File

@ -3,10 +3,10 @@
{{ template "logo.tpl.html" }}
</div>
<div class="menu-item flex items-center text-sm font-semibold space-x-2 rounded hover:bg-gray-850 py-2 px-4 cursor-pointer">
<a class="menu-item flex items-center text-sm font-semibold space-x-2 rounded hover:bg-gray-850 py-2 px-4 cursor-pointer" href="summary">
<span class="iconify inline text-2xl text-gray-400" data-icon="ic:round-dashboard"></span>
<a class="text-gray-300" href="summary">Dashboard</a>
</div>
<span class="text-gray-300">Dashboard</span>
</a>
<div class="menu-item flex items-center text-sm font-semibold space-x-2 rounded hover:bg-gray-850 py-2 px-4 cursor-not-allowed">
<span class="iconify inline text-2xl text-gray-700" data-icon="bi:people-fill"></span>
@ -32,7 +32,7 @@
<div class="hidden flex bg-gray-850 shadow-md z-10 p-2 absolute top-0 right-0 rounded popup mt-12 w-full" id="resources-menu-dropdown">
<div class="flex-grow flex flex-col">
<div class="submenu-item hover:bg-gray-800 rounded p-1 text-right">
<a class="flex justify-between w-full text-gray-300 items-center px-2 font-semibold" href="https://github.com/muety/wakapi" rel="noreferrer noopener" onclick="togglePopup(event, 'resources-menu-dropdown')">
<a class="flex justify-between w-full text-gray-300 items-center px-2 font-semibold" href="https://github.com/muety/wakapi" target="_blank" rel="noreferrer noopener" onclick="togglePopup(event, 'resources-menu-dropdown')">
<span class="text-sm">GitHub</span>
<span class="iconify inline" data-icon="codicon:github-inverted"></span>
</a>
@ -44,13 +44,13 @@
</a>
</div>
<div class="submenu-item hover:bg-gray-800 rounded p-1 text-right">
<a class="flex justify-between w-full text-gray-300 items-center px-2 font-semibold" href="https://wakatime.com" rel="noreferrer noopener" onclick="togglePopup(event, 'resources-menu-dropdown')">
<a class="flex justify-between w-full text-gray-300 items-center px-2 font-semibold" href="https://wakatime.com" target="_blank" rel="noreferrer noopener" onclick="togglePopup(event, 'resources-menu-dropdown')">
<span class="text-sm">WakaTime</span>
<span class="iconify inline" data-icon="simple-icons:wakatime"></span>
</a>
</div>
<div class="submenu-item hover:bg-gray-800 rounded p-1 text-right">
<a class="flex justify-between w-full text-gray-300 items-center px-2 font-semibold" href="https://github.com/sponsors/muety" rel="noreferrer noopener" onclick="togglePopup(event, 'resources-menu-dropdown')">
<a class="flex justify-between w-full text-gray-300 items-center px-2 font-semibold" href="https://github.com/sponsors/muety" target="_blank" rel="noreferrer noopener" onclick="togglePopup(event, 'resources-menu-dropdown')">
<span class="text-sm">Donate</span>
<span class="iconify inline" data-icon="bx:bxs-heart"></span>
</a>
@ -59,10 +59,10 @@
</div>
</div>
<div class="menu-item flex items-center text-sm font-semibold space-x-2 rounded hover:bg-gray-850 py-2 px-4 cursor-pointer">
<a class="menu-item flex items-center text-sm font-semibold space-x-2 rounded hover:bg-gray-850 py-2 px-4 cursor-pointer" href="settings">
<span class="iconify inline text-2xl text-gray-400" data-icon="ci:settings-filled"></span>
<a class="text-gray-400" href="settings">Settings</a>
</div>
<span class="text-gray-400">Settings</span>
</a>
<div class="flex-grow"></div>

View File

@ -4,6 +4,25 @@
{{ template "head.tpl.html" . }}
<script src="assets/timezones.js"></script>
<script type="module">
const defaultTab = 'account'
PetiteVue.createApp({
$delimiters: ['${', '}'],
activeTab: defaultTab,
updateTab() {
this.activeTab = window.location.hash.slice(1) || defaultTab
},
isActive(tab) {
return this.activeTab === tab
},
mounted() {
this.updateTab()
window.addEventListener('hashchange', () => this.updateTab())
}
}).mount()
</script>
<body class="bg-gray-900 text-gray-700 p-4 pt-10 flex flex-col min-h-screen max-w-screen-xl mx-auto justify-center">
<style>
@ -20,52 +39,71 @@
}
</style>
{{ template "header.tpl.html" . }}
<div class="w-full flex justify-center">
<div class="flex items-center justify-between max-w-2xl flex-grow">
<div><a href="" class="text-gray-500 text-sm cursor-pointer">&larr; Go back</a></div>
<div><h1 class="font-semibold text-2xl text-white m-0 border-b-4 border-green-700">Settings</h1></div>
<div>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</div>
</div>
</div>
{{ template "menu-main.tpl.html" . }}
{{ template "alerts.tpl.html" . }}
<main class="mt-4 flex-grow flex justify-center w-full">
<div class="flex flex-col flex-grow max-w-2xl mt-8">
<main class="mt-10 flex-grow flex justify-center w-full" v-scope @vue:mounted="mounted">
<div class="flex flex-col flex-grow mt-10">
<h1 class="font-semibold text-3xl text-white m-0 mb-4">Settings</h1>
<details class="my-8 pb-8 border-b border-gray-700" id="details-account">
<summary class="cursor-pointer">
<h2 class="font-semibold text-lg text-white m-0 border-b-2 border-green-700 inline-block"
id="preferences-heading">
Account Preferences
</h2>
</summary>
<div class="w-full">
<form class="mt-10" action="" method="post">
<ul class="flex space-x-4 mb-16 text-gray-600">
<li class="font-semibold text-2xl" v-bind:class="{ 'text-gray-300': isActive('account'), 'hover:text-gray-500': !isActive('account') }">
<a href="settings#account" @click="updateTab">Account</a>
</li>
<li class="font-semibold text-2xl" v-bind:class="{ 'text-gray-300': isActive('data'), 'hover:text-gray-500': !isActive('data') }">
<a href="settings#data" @click="updateTab">Data</a>
</li>
<li class="font-semibold text-2xl" v-bind:class="{ 'text-gray-300': isActive('permissions'), 'hover:text-gray-500': !isActive('permissions') }">
<a href="settings#permissions" @click="updateTab">Permissions</a>
</li>
<li class="font-semibold text-2xl" v-bind:class="{ 'text-gray-300': isActive('integrations'), 'hover:text-gray-500': !isActive('integrations') }">
<a href="settings#integrations" @click="updateTab">Integrations</a>
</li>
<li class="font-semibold text-2xl" v-bind:class="{ 'text-gray-300': isActive('danger_zone'), 'hover:text-gray-500': !isActive('danger_zone') }">
<a href="settings#danger_zone" @click="updateTab">Danger Zone</a>
</li>
</ul>
<div id="account" class="tab flex flex-col space-y-4" v-if="isActive('account')">
<!-- Account Settings -->
<form action="" method="post" class="w-3/4">
<input type="hidden" name="action" value="update_user">
<div class="mb-8 flex justify-between items-center space-x-4">
<label class="inline-block text-sm text-gray-500 w-1/3" for="select-timezone">Time Zone</label>
<div class="flex mb-8">
<div class="w-1/2 mr-4 inline-block">
<label class="font-semibold text-gray-300" for="select-timezone">Time Zone</label>
<span class="block text-sm text-gray-600">Time Zone, which you are located in. Relevant for displaying daily statistics.</span>
</div>
<div class="w-1/2 ml-4">
<select name="location" id="select-timezone"
class="shadow appearance-nonshadow appearance-none bg-gray-800 focus:bg-gray-700 text-gray-300 border-green-700 focus:border-gray-500 border rounded flex-grow py-1 px-3 cursor-pointer">
class="appearance-none bg-gray-850 focus:bg-gray-800 text-gray-300 outline-none rounded py-2 px-4 w-full cursor-pointer">
</select>
</div>
</div>
<div class="mb-8 flex justify-between items-center space-x-4">
<label class="inline-block text-sm text-gray-500 w-1/3" for="email">E-Mail Address</label>
<input class="shadow appearance-none bg-gray-800 focus:bg-gray-700 text-gray-300 border-green-700 focus:border-gray-500 border rounded flex-grow py-1 px-3"
<div class="flex mb-8">
<div class="w-1/2 mr-4 inline-block">
<label class="font-semibold text-gray-300" for="email">E-Mail Address</label>
<span class="block text-sm text-gray-600">Optional in general, but required for weekly reports and for resetting your password.</span>
</div>
<div class="w-1/2 ml-4">
<input class="appearance-none bg-gray-850 focus:bg-gray-800 text-gray-300 outline-none rounded py-2 px-4 w-full"
type="email" id="email"
name="email" placeholder="Enter your e-mail address"
value="{{ .User.Email }}">
</div>
</div>
{{ if .User.Email }}
<div class="flex items-center w-full text-gray-500 text-sm my-2 w-1/3 space-x-4">
<span class="inline-block text-sm text-gray-500 w-1/3">Weekly E-Mail Reports</span>
<div class="justify-start">
<div class="flex mb-8">
<div class="w-1/2 mr-4 inline-block">
<label class="font-semibold text-gray-300" for="reports_weekly">Weekly E-Mail Reports</label>
<span class="block text-sm text-gray-600">Opt in to receive a summary of your coding activity once a week.</span>
</div>
<div class="w-1/2 ml-4">
<select autocomplete="off" name="reports_weekly"
class="cursor-pointer shadow appearance-nonshadow appearance-none bg-gray-800 focus:bg-gray-700 text-gray-300 border-green-700 focus:border-gray-500 border rounded py-1 px-3">
class="appearance-none bg-gray-850 focus:bg-gray-800 text-gray-300 outline-none rounded py-2 px-4 w-full cursor-pointer">
<option value="false" class="cursor-pointer" {{ if not .User.ReportsWeekly }} selected{{ end }}>Disabled</option>
<option value="true" class="cursor-pointer" {{ if .User.ReportsWeekly }} selected {{ end }}>Enabled</option>
</select>
@ -73,162 +111,164 @@
</div>
{{ end }}
<div class="text-gray-300 text-sm mt-8">E-Mail address is optional, but required for some features
that you cannot use else. Also, if you do not add an e-mail address, you will not be able to
reset your password in case you forget it.
</div>
<div class="flex justify-end mt-4">
<button type="submit"
class="py-1 px-3 rounded bg-green-700 hover:bg-green-800 text-white text-sm self-end">
<button type="submit" class="py-2 px-4 font-semibold rounded bg-green-700 hover:bg-green-800 text-white text-sm">
Save
</button>
</div>
</form>
</div>
</details>
<details class="mb-8 pb-8 border-b border-gray-700" id="details-password">
<summary class="cursor-pointer">
<h2 class="font-semibold text-lg text-white m-0 border-b-2 border-green-700 inline-block" id="password">
Change Password
</h2>
</summary>
<div class="w-full">
<form class="mt-10" action="" method="post">
<div class="w-3/4">
<hr class="border-t border-gray-800 my-4">
</div>
<!-- Password -->
<form class="w-3/4" action="" method="post">
<input type="hidden" name="action" value="change_password">
<div class="mb-8">
<label class="inline-block text-sm mb-1 text-gray-500" for="password_old">Current
Password</label>
<input class="shadow appearance-none bg-gray-800 focus:bg-gray-700 text-gray-300 border-green-700 focus:border-gray-500 border rounded w-full py-1 px-3"
<div class="flex mb-8">
<div class="w-1/2 mr-4 inline-block">
<label class="font-semibold text-gray-300" for="password_old">Current Password</label>
<span class="block text-sm text-gray-600">Enter your old password for verification.</span>
</div>
<div class="w-1/2 ml-4">
<input class="appearance-none bg-gray-850 focus:bg-gray-800 text-gray-300 outline-none rounded py-2 px-4 w-full"
type="password" id="password_old"
name="password_old" placeholder="Enter your old password" minlength="6" required>
name="password_old" placeholder="Old password" minlength="6" required>
</div>
<div class="mb-8">
<label class="inline-block text-sm mb-1 text-gray-500" for="password_new">New Password</label>
<input class="shadow appearance-none bg-gray-800 focus:bg-gray-700 text-gray-300 border-green-700 focus:border-gray-500 border rounded w-full py-1 px-3"
</div>
<div class="flex mb-8">
<div class="w-1/2 mr-4 inline-block">
<label class="font-semibold text-gray-300" for="password_new">New Password</label>
<span class="block text-sm text-gray-600">Choose a new password. Preferably, it is at least 8 characters long and contains letters, digits and special chars.</span>
</div>
<div class="w-1/2 ml-4">
<input class="appearance-none bg-gray-850 focus:bg-gray-800 text-gray-300 outline-none rounded py-2 px-4 w-full"
type="password" id="password_new"
name="password_new" placeholder="Choose a password" minlength="6" required>
name="password_new" placeholder="New password" minlength="6" required>
</div>
<div class="mb-8">
<label class="inline-block text-sm mb-1 text-gray-500" for="password_repeat">And again
...</label>
<input class="shadow appearance-none bg-gray-800 focus:bg-gray-700 text-gray-300 border-green-700 focus:border-gray-500 border rounded w-full py-1 px-3"
</div>
<div class="flex mb-8">
<div class="w-1/2 mr-4 inline-block">
<label class="font-semibold text-gray-300" for="password_repeat">Repeat Password</label>
<span class="block text-sm text-gray-600">Once again ...</span>
</div>
<div class="w-1/2 ml-4">
<input class="appearance-none bg-gray-850 focus:bg-gray-800 text-gray-300 outline-none rounded py-2 px-4 w-full"
type="password" id="password_repeat"
name="password_repeat" placeholder="Repeat your password" minlength="6" required>
</div>
<div class="flex justify-between float-right">
<button type="submit"
class="py-1 px-3 rounded bg-green-700 hover:bg-green-800 text-white text-sm">
</div>
<div class="flex justify-end mt-4">
<button type="submit" class="py-2 px-4 font-semibold rounded bg-green-700 hover:bg-green-800 text-white text-sm">
Save
</button>
</div>
</form>
</div>
</details>
<details class="mb-8 pb-8 border-b border-gray-700" id="details-aliases">
<summary class="cursor-pointer">
<h2 class="font-semibold text-lg text-white m-0 border-b-2 border-green-700 inline-block">
Aliases
</h2>
</summary>
<div class="w-full" id="aliases">
<div class="text-gray-300 text-sm mb-4 mt-6">
You can specify aliases for any type of entity. For instance, you can define a rule, that both <span
class="inline-block mb-1 text-gray-500 italic">myapp-frontend</span> and <span
class="inline-block mb-1 text-gray-500 italic">myapp-backend</span> are combined under a
project called <span class="inline-block mb-1 text-gray-500 italic">myapp</span>.
<div id="data" class="tab flex flex-col space-y-4" v-if="isActive('data')">
<!-- Aliases -->
<div class="w-full">
<div class="flex mb-8">
<div class="w-1/3 mr-4 inline-block">
<span class="font-semibold text-gray-300 text-lg">Aliases</span>
<p class="block text-sm text-gray-600">You can specify aliases for any type of entity. For instance, you can define a rule, that both "myapp-frontend" and "myapp-backend" are combined under a project called "myapp".</p>
</div>
<div class="w-2/3 ml-4 inline-block">
{{ if .Aliases }}
<h3 class="inline-block font-semibold text-md border-b border-green-700 text-white">Rules</h3>
<div class="mb-8">
<h3 class="inline-block font-semibold text-gray-300">Rules</h3>
{{ range $i, $alias := .Aliases }}
<div class="flex items-center">
<div class="text-gray-500 border-1 w-full border-green-700 inline-block my-1 py-1 text-align text-sm"
<div class="text-gray-300 border-1 w-full inline-block my-1 py-1 text-align text-sm"
style="line-height: 1.8">
&#9656;&nbsp; All <span class="underline">{{ $alias.Type | typeName }}s</span> named
&#9656;&nbsp; All <span class="font-semibold">{{ $alias.Type | typeName }}s</span> named
{{ range $j, $value := $alias.Values }}
<span class="text-white text-xs bg-gray-900 rounded py-1 px-2 font-mono">{{- $value -}}</span>
<span class="text-white text-sm bg-gray-900 rounded py-1 px-2 font-semibold text-green-700">{{- $value -}}</span>
{{ if lt $j (add (len $alias.Values) -2) }}
<span class="-ml-1">{{- ", " | capitalize -}}</span>
{{ else if lt $j (add (len $alias.Values) -1) }}
<span>{{- "or" -}}</span>
{{ end }}
{{ end }}
are mapped to <span class="underline">{{ $alias.Type | typeName }}</span> <span
class="text-white text-xs bg-gray-900 rounded py-1 px-2 font-mono">{{ $alias.Key }}</span>.
are mapped to <span class="font-semibold">{{ $alias.Type | typeName }}</span> <span
class="text-white text-sm bg-gray-900 rounded py-1 px-2 font-semibold text-green-700">{{ $alias.Key }}</span>
</div>
<form class="float-right" action="" method="post">
<input type="hidden" name="action" value="delete_alias">
<input type="hidden" id="delete_alias_key" name="key" required value="{{ $alias.Key }}">
<input type="hidden" id="delete_alias_type" name="type" required value="{{ $alias.Type }}">
<button type="submit"
class="py-1 px-3 rounded border border-red-500 hover:border-red-600 text-gray-400 text-sm">
</button>
<button type="submit" class="py-2 px-4 rounded bg-gray-850 hover:bg-gray-800 text-red-600 text-sm" title="Delete rule"></button>
</form>
</div>
{{end}}
<div class="mb-8"></div>
</div>
{{end}}
<h3 class="inline-block font-semibold text-md border-b border-green-700 text-white mb-2">Add Rule</h3>
<form action="" method="post">
<h3 class="inline-block font-semibold text-gray-300">Add Rule</h3>
<input type="hidden" name="action" value="add_alias">
<div class="flex items-center mt-2 w-full text-gray-500 text-sm">
<span class="mr-2">Map</span>
<select name="type" id="select-type"
class="shadow appearance-nonshadow appearance-none bg-gray-800 focus:bg-gray-700 text-gray-300 border-green-700 focus:border-gray-500 border rounded py-1 px-3 cursor-pointer">
class="appearance-none bg-gray-850 focus:bg-gray-800 text-gray-300 outline-none rounded py-2 px-4 cursor-pointer">
{{ range $i, $t := entityTypes }}
<option value="{{ $t }}">{{ $t | typeName | capitalize }}</option>
{{ end }}
</select>
<span class="mx-2">named</span>
<input class="shadow appearance-nonshadow appearance-none bg-gray-800 focus:bg-gray-700 text-gray-300 border-green-700 focus:border-gray-500 border rounded py-1 px-3"
<input class="flex-grow appearance-none bg-gray-850 focus:bg-gray-800 text-gray-300 outline-none rounded py-2 px-4"
type="text" id="alias-value" style="width: 130px;"
name="value" placeholder="myapp-frontend" minlength="1" required>
name="value" placeholder="Original name" minlength="1" required>
<span class="mx-2">to</span>
<input class="shadow appearance-nonshadow appearance-none bg-gray-800 focus:bg-gray-700 text-gray-300 border-green-700 focus:border-gray-500 border rounded py-1 px-3"
<input class="flex-grow appearance-none bg-gray-850 focus:bg-gray-800 text-gray-300 outline-none rounded py-2 px-4"
type="text" id="alias-key" style="width: 100px"
name="key" placeholder="myapp" minlength="1" required>
<div class="flex-grow flex justify-end">
<button type="submit"
class="py-1 px-3 rounded bg-green-700 hover:bg-green-800 text-white text-sm">
name="key" placeholder="Replacement" minlength="1" required>
<div class="flex justify-end ml-4">
<button type="submit" class="py-2 px-4 font-semibold rounded bg-green-700 hover:bg-green-800 text-white text-sm">
Add
</button>
</div>
</div>
</form>
</div>
</details>
<details class="mb-8 pb-8 border-b border-gray-700" id="details-labels">
<summary class="cursor-pointer">
<h2 class="font-semibold text-lg text-white m-0 border-b-2 border-green-700 inline-block">
Project Labels
</h2>
</summary>
<div class="w-full" id="project-labels">
<div class="text-gray-300 text-sm mb-4 mt-6">
You can assign labels (aka. tags) to projects to group them together, e.g. by <span class="inline-block mb-1 text-gray-500 italic">private</span> and <span
class="inline-block mb-1 text-gray-500 italic">work</span>.
</div>
</div>
<div class="w-full">
<hr class="border-t border-gray-800 my-4">
</div>
<!-- Project Labels -->
<div class="w-full">
<div class="flex mb-8">
<div class="w-1/3 mr-4 inline-block">
<span class="font-semibold text-gray-300 text-lg">Project Labels</span>
<p class="block text-sm text-gray-600">You can assign labels (aka. tags) to projects to group them together, e.g. by "private" and "work".</p>
</div>
<div class="w-2/3 ml-4 inline-block">
{{ if .Labels }}
<h3 class="inline-block font-semibold text-md border-b border-green-700 text-white">Labels</h3>
<div class="mb-8">
<h3 class="inline-block font-semibold text-gray-300">Labels</h3>
{{ range $i, $label := .Labels }}
<div class="flex items-center" action="" method="post">
<div class="text-gray-500 border-1 w-full border-green-700 inline-block my-1 py-1 text-align text-sm"
style="line-height: 1.8">
&#9656;&nbsp;<span class="font-semibold text-white font-mono">{{ $label.Key }}:</span>
&#9656;&nbsp;&nbsp;<span class="font-semibold text-gray-300">{{ $label.Key }}:</span>
{{ range $j, $value := $label.Values }}
<form action="" method="post" class="text-white text-xs bg-gray-900 rounded-full py-1 px-2 my-1 inline-flex justify-between items-center space-x-2">
<input type="hidden" name="action" value="delete_label">
<input type="hidden" name="key" value="{{ $label.Key }}">
<input type="hidden" name="value" value="{{ $value }}">
<span>{{- $value -}}</span>
<button type="submit" class="bg-gray-800 text-center hover:bg-gray-700 rounded-full w-4 h-4 leading-none" title="Delete label">x</button>
<button type="submit" class="bg-gray-800 text-center hover:bg-gray-700 rounded-full w-4 h-4 leading-none text-red-600" title="Delete label">x</button>
</form>
{{ if lt $j (add (len $label.Values) -1) }}
<span class="-ml-1">{{- ", " | capitalize -}}</span>
@ -238,33 +278,23 @@
</div>
{{end}}
<div class="mb-8"></div>
{{end}}
{{ if .Projects }}
<h3 class="inline-block font-semibold text-md border-b border-green-700 text-white mb-2">Add Label</h3>
<h3 class="inline-block font-semibold text-gray-300">Add Label</h3>
<form action="" method="post">
<input type="hidden" name="action" value="add_label">
<div class="flex flex-col space-y-4">
<div class="flex justify-between items-center mt-2 w-full text-gray-500 text-sm space-x-4">
<div class="w-1/2 flex flex-col flex-grow">
<span>Project</span>
<div class="flex items-center mt-2 w-full text-gray-500 text-sm space-x-4">
<select name="key" id="select-project"
class="shadow appearance-nonshadow appearance-none bg-gray-800 focus:bg-gray-700 text-gray-300 border-green-700 focus:border-gray-500 border rounded py-1 px-3 cursor-pointer">
class="flex-grow appearance-none bg-gray-850 focus:bg-gray-800 text-gray-300 outline-none rounded py-2 px-4 cursor-pointer">
{{ range $i, $p := .Projects }}
<option value="{{ $p }}">{{ $p }}</option>
{{ end }}
</select>
</div>
<div class="w-1/2 flex flex-col flex-grow">
<span>Label</span>
<input class="shadow appearance-nonshadow appearance-none bg-gray-800 focus:bg-gray-700 text-gray-300 border-green-700 focus:border-gray-500 border rounded py-1 px-3"
<input class="flex-grow appearance-none bg-gray-850 focus:bg-gray-800 text-gray-300 outline-none rounded py-2 px-4"
type="text" id="label-value"
name="value" placeholder="work" minlength="1" required>
</div>
</div>
<div class="flex-grow flex justify-end">
<button type="submit"
class="py-1 px-3 rounded bg-green-700 hover:bg-green-800 text-white text-sm">
name="value" placeholder="Label" minlength="1" required>
<button type="submit" class="py-2 px-4 font-semibold rounded bg-green-700 hover:bg-green-800 text-white text-sm">
Add
</button>
</div>
@ -274,68 +304,72 @@
<div class="text-gray-300 text-sm mb-4 mt-6">You don't have any projects, yet. Start out by sending a few heartbeats before you can then assign labels.</div>
{{ end }}
</div>
</details>
<details class="mb-8 pb-8 border-b border-gray-700" id="details-mappings">
<summary class="cursor-pointer">
<h2 class="font-semibold text-lg text-white m-0 border-b-2 border-green-700 inline-block"
id="languages">
Custom Mappings
</h2>
</summary>
<div class="w-full">
<div class="text-gray-300 text-sm mb-4 mt-6">
You can specify custom mapping from file extensions to programming languages, for instance a <span
class="inline-block mb-1 text-gray-500 italic">.jsx</span> file could be mapped to the <span
class="inline-block mb-1 text-gray-500 italic">React</span> language.
{{end}}
</div>
</div>
</div>
<div class="w-full">
<hr class="border-t border-gray-800 my-4">
</div>
<!-- Language Mappings -->
<div class="w-full">
<div class="flex mb-8">
<div class="w-1/3 mr-4 inline-block">
<span class="font-semibold text-gray-300 text-lg">Language Mappings</span>
<p class="block text-sm text-gray-600">You can specify custom mapping from file extensions to programming languages, for instance a ".jsx" file could be mapped to the "React" language.</p>
</div>
<div class="w-2/3 ml-4 inline-block">
{{ if .LanguageMappings }}
<h3 class="inline-block font-semibold text-md border-b border-green-700 text-white">Rules</h3>
<div class="mb-8">
<h3 class="inline-block font-semibold text-gray-300">Rules</h3>
{{ range $i, $mapping := .LanguageMappings }}
<div class="flex items-center">
<div class="text-gray-500 border-1 w-full border-green-700 inline-block my-1 py-1 text-align text-sm">
<div class="text-gray-300 border-1 w-full inline-block my-1 py-1 text-align text-sm">
&#9656;&nbsp; When filename ends in <span
class="text-white text-xs bg-gray-900 rounded py-1 px-2 font-mono mr-1">{{ $mapping.Extension }}</span>
then change the <span class="underline">language</span> to <span
class="text-white text-xs bg-gray-900 rounded py-1 px-2 font-mono mr-1">{{ $mapping.Language }}</span>
class="text-green-700 text-sm bg-gray-900 rounded py-1 px-2 font-semibold mr-1">{{ $mapping.Extension }}</span>
then change the <span class="font-semibold">language</span> to <span
class="text-green-700 text-sm bg-gray-900 rounded py-1 px-2 font-semibold mr-1">{{ $mapping.Language }}</span>
</div>
<form class="float-right" action="" method="post">
<input type="hidden" name="action" value="delete_mapping">
<input type="hidden" id="mapping_id" name="mapping_id" required value="{{ $mapping.ID }}">
<button type="submit"
class="py-1 px-3 rounded border border-red-500 hover:border-red-600 text-gray-400 text-sm">
</button>
<button type="submit" class="py-2 px-4 rounded bg-gray-850 hover:bg-gray-800 text-red-600 text-sm" title="Delete rule"></button>
</form>
</div>
{{end}}
<div class="mb-8"></div>
</div>
{{end}}
<h3 class="inline-block font-semibold text-md border-b border-green-700 text-white">Add Rule</h3>
<form action="" method="post">
<h3 class="inline-block font-semibold text-gray-300">Add Rule</h3>
<input type="hidden" name="action" value="add_mapping">
<div class="flex items-center w-full text-gray-500 text-sm">
<span class="mr-2">When filename ends in</span>
<input class="shadow appearance-nonshadow appearance-none bg-gray-800 focus:bg-gray-700 text-gray-300 border-green-700 focus:border-gray-500 border rounded py-1 px-3"
<input class="flex-grow appearance-none bg-gray-850 focus:bg-gray-800 text-gray-300 outline-none rounded py-2 px-4 cursor-pointer"
type="text" id="extension" style="width: 70px"
name="extension" placeholder=".py" minlength="1" required>
<span class="mx-2">change language to</span>
<input class="shadow appearance-nonshadow appearance-none bg-gray-800 focus:bg-gray-700 text-gray-300 border-green-700 focus:border-gray-500 border rounded py-1 px-3"
<input class="flex-grow appearance-none bg-gray-850 focus:bg-gray-800 text-gray-300 outline-none rounded py-2 px-4 cursor-pointer"
type="text" id="language" style="width: 100px"
name="language" placeholder="Python" minlength="1" required>
<div class="flex-grow flex justify-end">
<button type="submit"
class="py-1 px-3 my-3 rounded bg-green-700 hover:bg-green-800 text-white text-sm">
<div class="flex justify-end ml-4">
<button type="submit" class="py-2 px-4 font-semibold rounded bg-green-700 hover:bg-green-800 text-white text-sm">
Add
</button>
</div>
</div>
</form>
</div>
</details>
</div>
</div>
</div>
<div id="permissions" class="tab flex flex-col space-y-4" v-if="isActive('permissions')">
<!-- TODO -->
<details class="mb-8 pb-8 border-b border-gray-700" id="details-public-data">
<summary class="cursor-pointer">
<h2 class="font-semibold text-lg text-white m-0 border-b-2 border-green-700 inline-block">
@ -460,7 +494,10 @@
</form>
</div>
</details>
</div>
<div id="integrations" class="tab flex flex-col space-y-4" v-if="isActive('integrations')">
<!-- TODO -->
<details class="mb-8 pb-8 border-b border-gray-700" id="details-integrations">
<summary class="cursor-pointer">
<h2 class="font-semibold text-lg text-white m-0 border-b-2 border-green-700 inline-block"
@ -617,7 +654,10 @@
</div>
</div>
</details>
</div>
<div id="danger_zone" class="tab flex flex-col space-y-4" v-if="isActive('danger_zone')">
<!-- TODO -->
<details class="mb-8 pb-8" id="details-danger-zone">
<summary class="cursor-pointer">
<h2 class="font-semibold text-lg text-white m-0 border-b-2 border-green-700 inline-block" id="danger">
@ -704,6 +744,7 @@
</div>
</details>
</div>
</div>
</main>
<script type="text/javascript">

View File

@ -12,7 +12,7 @@
<main class="mt-10 flex-grow flex justify-center w-full">
<div class="flex-grow max-w-lg mt-10">
<div class="mb-8">
<h1 class="font-semibold text-2xl text-white m-0 mb-2">Sign up to Wakapi</h1>
<h1 class="font-semibold text-3xl text-white m-0 mb-2">Sign up to Wakapi</h1>
<p class="text-sm text-gray-600">
Welcome to Wakapi! Your first step is to create an account.
Afterwards, make sure to set up the <a href="https://wakatime.com" target="_blank" rel="noopener noreferrer" class="text-gray-300 hover:text-gray-400">WakaTime</a> client tools.

View File

@ -7,6 +7,8 @@
{{ template "menu-main.tpl.html" . }}
{{ template "alerts.tpl.html" . }}
{{ if .User.HasData }}
<div class="flex justify-end mt-12 relative">
@ -46,8 +48,6 @@
{{ end }}
{{ template "alerts.tpl.html" . }}
<main class="flex flex-col items-center mt-10 flex-grow">
{{ if .User.HasData }}
@ -161,7 +161,7 @@
<div class="p-4 px-6 pb-10 bg-gray-850 text-gray-300 rounded-md shadow flex flex-col" id="label-container" style="height: 300px">
<div class="flex justify-between text-lg">
<span class="font-semibold whitespace-no-wrap">Labels</span>
<a href="settings#details-labels" class="ml-4 h-8 inline flex-grow">
<a href="settings#data" class="ml-4 inline p-2 hover:bg-gray-800 rounded" style="margin-top: -5px">
<span class="iconify inline" data-icon="twemoji:gear"></span>
</a>
<div class="flex justify-end flex-1 text-xs items-center">
@ -182,12 +182,12 @@
<div class="pb-4">
<img src="assets/images/welcome.svg" width="200px" alt="User welcome illustration">
</div>
<h1 class="font-semibold text-2xl text-white m-0 w-full">Welcome to Wakapi!</h1>
<h1 class="font-semibold text-3xl text-white m-0 w-full">Welcome to Wakapi!</h1>
<p>
It looks like there is no data available for the specified time range.<br>If you logged in to Wakapi for the first time, see the setup instructions below on how to get started.
</p>
<div class="w-full pt-10 flex flex-col space-y-4">
<h1 class="font-semibold text-2xl text-white m-0 mb-2">Setup Instructions</h1>
<h1 class="font-semibold text-3xl text-white m-0 mb-2">Setup Instructions</h1>
<div class="w-full bg-gray-850 text-left rounded-md py-4 px-8 text-xs font-mono shadow-md">
# <strong>Step 1:</strong> Download WakaTime plugin for your IDE<br>
# See: https://wakatime.com/plugins<br><br>
@ -238,8 +238,6 @@
}
</script>
<script src="assets/app.js"></script>
</body>
</html>