1
0
mirror of https://github.com/muety/wakapi.git synced 2023-08-10 21:12:56 +03:00

Compare commits

...

19 Commits
2.3.6 ... 2.3.8

Author SHA1 Message Date
cec2a84e2d Merge pull request #402 from muety/release-arch
Add more architectures to release binaries (resolve #401)
2022-09-06 22:16:35 +02:00
ffb0b84d78 build: re-enable release steps 2022-09-06 20:51:05 +10:00
8a7333b899 build: test release architectures 2022-09-06 20:23:47 +10:00
dd3b9c9b9c Merge pull request #398 from marvinscham/patch-1
Add last_6_months interval identifier
2022-08-21 10:24:03 +02:00
d2b62e21c5 Fix indentation 2022-08-20 07:07:44 +02:00
9505773165 Add last_6_months interval identifier 2022-08-19 17:14:00 +02:00
4bfc8a9e9f Merge pull request #397 from muety/docker-simplify
Simplify Dockerfile
2022-08-19 13:59:33 +02:00
df5fe6e623 fix: initialise data/ dir in Docker 2022-08-19 21:55:58 +10:00
037ad7b9b1 fix: check database open 2022-08-19 21:48:03 +10:00
ec10cc922c perf: strip binaries 2022-08-19 19:53:02 +10:00
acb76e1ab1 ref: simplify Dockerfile stages 2022-08-19 19:37:05 +10:00
252a304ba8 Merge pull request #396 from HellsCrimson/master
add last_year and all_time interval identifier like in wakatime docs
2022-08-17 18:50:07 +10:00
c863cf6dc5 add last_year and all_time interval identifier like in wakatime docs 2022-08-16 23:28:48 +02:00
373d969734 feat: introduce newsbox for front page (resolve #393) 2022-08-13 10:28:36 +02:00
99a3e8f5da Merge pull request #394 from muety/nil-fix
fix: 500 on POST /api/heartbeats
2022-08-12 14:37:33 +02:00
4302cfcbd6 fix: 500 on POST /api/heartbeats 2022-08-12 17:25:43 +10:00
7cca0055fe Merge pull request #388 from muety/automated-version
feat: automatic version.txt updates
2022-07-14 14:31:06 +02:00
20993a1182 feat: automatic version.txt updates
Resolves #387
2022-07-09 13:58:16 +10:00
d5a85639b1 fix: broken summary aggregation (resolve #385) 2022-07-03 20:39:16 +02:00
27 changed files with 208 additions and 96 deletions

View File

@ -11,6 +11,15 @@ jobs:
name: 'Build and publish Docker image'
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v3
- name: Set version
run: |
(git describe --tags --exact-match \
|| git symbolic-ref -q --short HEAD \
|| git rev-parse --short HEAD) > version.txt 2> /dev/null
- name: Set up QEMU
uses: docker/setup-qemu-action@v1
@ -47,6 +56,7 @@ jobs:
- name: Build and push
uses: docker/build-push-action@v2
with:
context: .
file: Dockerfile
push: true
platforms: linux/amd64,linux/arm64,linux/arm/v7

View File

@ -8,20 +8,13 @@ on:
jobs:
release:
name: 'Build, package and release to GitHub'
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
platform: [ubuntu-latest, macos-latest, windows-latest]
include:
- platform: ubuntu-latest
alias: linux
- platform: macos-latest
alias: mac
- platform: windows-latest
alias: win
GOOS: [darwin, windows, linux]
GOARCH: [amd64, arm64]
runs-on: ${{ matrix.platform }}
steps:
- name: Set up Go 1.x
@ -33,24 +26,29 @@ jobs:
- name: Check out code into the Go module directory
uses: actions/checkout@v2
- name: Get dependencies
run: go get
- name: Set version
shell: bash
run: |
(git describe --tags --exact-match \
|| git symbolic-ref -q --short HEAD \
|| git rev-parse --short HEAD) > version.txt 2> /dev/null
- name: Prepare
run: |
mkdir -p dist/ && cd dist/
cp ../config.default.yml config.yml
- name: Build
run: go build -v .
working-directory: ./dist
run: |
GOOS=${{ matrix.GOOS }} GOARCH=${{ matrix.GOARCH }} go build -v ../
- name: Compress working folder on Windows
if: runner.os == 'Windows'
- name: Compress working folder
working-directory: ./dist
run: |
cp .\config.default.yml .\config.yml
Compress-Archive -Path .\wakapi.exe, .\config.yml -DestinationPath wakapi_${{ matrix.alias }}_amd64.zip
- name: Compress working folder on Unix
if: runner.os != 'Windows'
run: |
cp config.default.yml config.yml
zip -9 wakapi_${{ matrix.alias }}_amd64.zip wakapi config.yml
zip -9 wakapi_${{ matrix.GOOS }}_${{ matrix.GOARCH }}.zip *
- name: Upload built executable to Release
uses: softprops/action-gh-release@v1
with:
files: wakapi_${{ matrix.alias }}_amd64.zip
files: ./dist/*.zip

View File

@ -1,33 +1,25 @@
# To build locally: docker buildx build . -t wakapi --load
# Preparation to save some time
FROM --platform=$BUILDPLATFORM golang:1.18-alpine AS prep-env
WORKDIR /src
ADD ./go.mod .
RUN go mod download
ADD . .
RUN wget "https://raw.githubusercontent.com/vishnubob/wait-for-it/master/wait-for-it.sh" -O wait-for-it.sh && \
chmod +x wait-for-it.sh
# Build Stage
FROM golang:1.18-alpine AS build-env
WORKDIR /src
# Required for go-sqlite3
RUN apk add --no-cache gcc musl-dev
WORKDIR /src
COPY --from=prep-env /src .
RUN wget "https://raw.githubusercontent.com/vishnubob/wait-for-it/master/wait-for-it.sh" -O wait-for-it.sh && \
chmod +x wait-for-it.sh
RUN go build -v -o wakapi
ADD ./go.mod ./go.sum ./
RUN go mod download
ADD . .
WORKDIR /app
RUN cp /src/wakapi . && \
cp /src/config.default.yml config.yml && \
sed -i 's/listen_ipv6: ::1/listen_ipv6: /g' config.yml && \
cp /src/wait-for-it.sh . && \
cp /src/entrypoint.sh .
RUN go build -ldflags "-s -w" -v -o wakapi main.go
WORKDIR /staging
RUN mkdir ./data ./app && \
cp /src/wakapi app/ && \
cp /src/config.default.yml app/config.yml && \
sed -i 's/listen_ipv6: ::1/listen_ipv6: /g' app/config.yml && \
cp /src/wait-for-it.sh app/ && \
cp /src/entrypoint.sh app/
# Run Stage
@ -41,18 +33,18 @@ WORKDIR /app
RUN apk add --no-cache bash ca-certificates tzdata
# See README.md and config.default.yml for all config options
ENV ENVIRONMENT prod
ENV WAKAPI_DB_TYPE sqlite3
ENV WAKAPI_DB_USER ''
ENV WAKAPI_DB_PASSWORD ''
ENV WAKAPI_DB_HOST ''
ENV WAKAPI_DB_NAME=/data/wakapi.db
ENV WAKAPI_PASSWORD_SALT ''
ENV WAKAPI_LISTEN_IPV4 '0.0.0.0'
ENV WAKAPI_INSECURE_COOKIES 'true'
ENV WAKAPI_ALLOW_SIGNUP 'true'
ENV ENVIRONMENT=prod \
WAKAPI_DB_TYPE=sqlite3 \
WAKAPI_DB_USER='' \
WAKAPI_DB_PASSWORD='' \
WAKAPI_DB_HOST='' \
WAKAPI_DB_NAME=/data/wakapi.db \
WAKAPI_PASSWORD_SALT='' \
WAKAPI_LISTEN_IPV4='0.0.0.0' \
WAKAPI_INSECURE_COOKIES='true' \
WAKAPI_ALLOW_SIGNUP='true'
COPY --from=build-env /app .
COPY --from=build-env /staging /
EXPOSE 3000

View File

@ -4,10 +4,10 @@ import (
"encoding/json"
"flag"
"fmt"
uuid "github.com/satori/go.uuid"
"io/ioutil"
"net/http"
"os"
"regexp"
"strings"
"time"
@ -16,6 +16,7 @@ import (
"github.com/jinzhu/configor"
"github.com/muety/wakapi/data"
"github.com/muety/wakapi/models"
uuid "github.com/satori/go.uuid"
"gorm.io/gorm"
)
@ -29,6 +30,7 @@ const (
KeyLatestTotalTime = "latest_total_time"
KeyLatestTotalUsers = "latest_total_users"
KeyLastImportImport = "last_import"
KeyNewsbox = "newsbox"
SimpleDateFormat = "2006-01-02"
SimpleDateTimeFormat = "2006-01-02 15:04:05"
@ -363,7 +365,13 @@ func Load(version string) *Config {
}
env = config.Env
config.Version = strings.TrimSpace(version)
tagVersionMatch, _ := regexp.MatchString(`\d+\.\d+\.\d+`, version)
if tagVersionMatch {
config.Version = "v" + config.Version
}
config.InstanceId = uuid.NewV4().String()
config.App.Colors = readColors()
config.Db.Dialect = resolveDbDialect(config.Db.Type)

26
main.go
View File

@ -2,7 +2,6 @@ package main
import (
"embed"
"github.com/muety/wakapi/migrations"
"io/fs"
"log"
"net"
@ -11,28 +10,29 @@ import (
"strconv"
"time"
"github.com/lpar/gzipped/v2"
"github.com/muety/wakapi/routes/relay"
"github.com/emvi/logbuch"
"github.com/gorilla/handlers"
conf "github.com/muety/wakapi/config"
"github.com/muety/wakapi/repositories"
"github.com/muety/wakapi/routes/api"
"github.com/muety/wakapi/services/mail"
fsutils "github.com/muety/wakapi/utils/fs"
"gorm.io/gorm/logger"
"github.com/gorilla/mux"
"github.com/lpar/gzipped/v2"
conf "github.com/muety/wakapi/config"
"github.com/muety/wakapi/middlewares"
"github.com/muety/wakapi/migrations"
"github.com/muety/wakapi/repositories"
"github.com/muety/wakapi/routes"
"github.com/muety/wakapi/routes/api"
shieldsV1Routes "github.com/muety/wakapi/routes/compat/shields/v1"
wtV1Routes "github.com/muety/wakapi/routes/compat/wakatime/v1"
"github.com/muety/wakapi/routes/relay"
"github.com/muety/wakapi/services"
"github.com/muety/wakapi/services/mail"
fsutils "github.com/muety/wakapi/utils/fs"
_ "gorm.io/driver/mysql"
_ "gorm.io/driver/postgres"
_ "gorm.io/driver/sqlite"
"gorm.io/gorm"
"gorm.io/gorm/logger"
)
// Embed version.txt
@ -121,6 +121,10 @@ func main() {
// Connect to database
var err error
db, err = gorm.Open(config.Db.GetDialector(), &gorm.Config{Logger: gormLogger})
if err != nil {
logbuch.Error(err.Error())
logbuch.Fatal("could not open database")
}
if config.Db.IsSQLite() {
db.Exec("PRAGMA foreign_keys = ON;")
}

View File

@ -14,8 +14,9 @@ var (
IntervalPast7DaysYesterday = &IntervalKey{"Last 7 Days from Yesterday"}
IntervalPast14Days = &IntervalKey{"Last 14 Days"}
IntervalPast30Days = &IntervalKey{"30_days", "last_30_days", "Last 30 Days"}
IntervalPast12Months = &IntervalKey{"12_months", "last_12_months"}
IntervalAny = &IntervalKey{"any"}
IntervalPast6Months = &IntervalKey{"6_months", "last_6_months"}
IntervalPast12Months = &IntervalKey{"12_months", "last_12_months", "last_year"}
IntervalAny = &IntervalKey{"any", "all_time"}
)
var AllIntervals = []*IntervalKey{
@ -30,6 +31,7 @@ var AllIntervals = []*IntervalKey{
IntervalPast7DaysYesterday,
IntervalPast14Days,
IntervalPast30Days,
IntervalPast6Months,
IntervalPast12Months,
IntervalAny,
}

View File

@ -1,10 +1,16 @@
package view
type Newsbox struct {
Type string `json:"type"`
Text string `json:"text"`
}
type HomeViewModel struct {
Success string
Error string
TotalHours int
TotalUsers int
Newsbox *Newsbox
}
func (s *HomeViewModel) WithSuccess(m string) *HomeViewModel {

View File

@ -109,7 +109,7 @@ func (r *SummaryRepository) populateItems(summaries []*models.Summary, condition
}
for _, item := range items {
if _, ok := summaryMap[item.SummaryID]; ok {
if _, ok := summaryMap[item.SummaryID]; !ok {
continue
}
l := summaryMap[item.SummaryID][0].ItemsByType(item.Type)

View File

@ -79,6 +79,12 @@ func (h *HeartbeatApiHandler) Post(w http.ResponseWriter, r *http.Request) {
machineName := r.Header.Get("X-Machine-Name")
for _, hb := range heartbeats {
if hb == nil {
w.WriteHeader(http.StatusBadRequest)
w.Write([]byte("invalid heartbeat object"))
return
}
hb.OperatingSystem = opSys
hb.Editor = editor
hb.Machine = machineName

View File

@ -37,7 +37,7 @@ func (h *SummaryApiHandler) RegisterRoutes(router *mux.Router) {
// @ID get-summary
// @Tags summary
// @Produce json
// @Param interval query string false "Interval identifier" Enums(today, yesterday, week, month, year, 7_days, last_7_days, 30_days, last_30_days, 12_months, last_12_months, any)
// @Param interval query string false "Interval identifier" Enums(today, yesterday, week, month, year, 7_days, last_7_days, 30_days, last_30_days, 6_months, last_6_months, 12_months, last_12_months, last_year, any, all_time)
// @Param from query string false "Start date (e.g. '2021-02-07')"
// @Param to query string false "End date (e.g. '2021-02-08')"
// @Param recompute query bool false "Whether to recompute the summary from raw heartbeat or use cache"

View File

@ -43,7 +43,7 @@ func (h *BadgeHandler) RegisterRoutes(router *mux.Router) {
// @Tags badges
// @Produce json
// @Param user path string true "User ID to fetch data for"
// @Param interval path string true "Interval to aggregate data for" Enums(today, yesterday, week, month, year, 7_days, last_7_days, 30_days, last_30_days, 12_months, last_12_months, any)
// @Param interval path string true "Interval to aggregate data for" Enums(today, yesterday, week, month, year, 7_days, last_7_days, 30_days, last_30_days, 6_months, last_6_months, 12_months, last_12_months, last_year, any, all_time)
// @Param filter path string true "Filter to apply (e.g. 'project:wakapi' or 'language:Go')"
// @Success 200 {object} v1.BadgeData
// @Router /compat/shields/v1/{user}/{interval}/{filter} [get]

View File

@ -48,7 +48,7 @@ func (h *StatsHandler) RegisterRoutes(router *mux.Router) {
// @Tags wakatime
// @Produce json
// @Param user path string true "User ID to fetch data for (or 'current')"
// @Param range path string false "Range interval identifier" Enums(today, yesterday, week, month, year, 7_days, last_7_days, 30_days, last_30_days, 12_months, last_12_months, any)
// @Param range path string false "Range interval identifier" Enums(today, yesterday, week, month, year, 7_days, last_7_days, 30_days, last_30_days, 6_months, last_6_months, 12_months, last_12_months, last_year, any, all_time)
// @Param project query string false "Project to filter by"
// @Param language query string false "Language to filter by"
// @Param editor query string false "Editor to filter by"

View File

@ -50,7 +50,7 @@ func (h *SummariesHandler) RegisterRoutes(router *mux.Router) {
// @Tags wakatime
// @Produce json
// @Param user path string true "User ID to fetch data for (or 'current')"
// @Param range query string false "Range interval identifier" Enums(today, yesterday, week, month, year, 7_days, last_7_days, 30_days, last_30_days, 12_months, last_12_months, any)
// @Param range query string false "Range interval identifier" Enums(today, yesterday, week, month, year, 7_days, last_7_days, 30_days, last_30_days, 6_months, last_6_months, 12_months, last_12_months, last_year, any, all_time)
// @Param start query string false "Start date (e.g. '2021-02-07')"
// @Param end query string false "End date (e.g. '2021-02-08')"
// @Param project query string false "Project to filter by"

View File

@ -1,6 +1,7 @@
package routes
import (
"encoding/json"
"fmt"
"github.com/gorilla/mux"
"github.com/gorilla/schema"
@ -10,6 +11,7 @@ import (
"github.com/muety/wakapi/services"
"net/http"
"strconv"
"strings"
"time"
)
@ -49,23 +51,29 @@ func (h *HomeHandler) GetIndex(w http.ResponseWriter, r *http.Request) {
func (h *HomeHandler) buildViewModel(r *http.Request) *view.HomeViewModel {
var totalHours int
var totalUsers int
var newsbox view.Newsbox
if t, err := h.keyValueSrvc.GetString(conf.KeyLatestTotalTime); err == nil && t != nil && t.Value != "" {
if d, err := time.ParseDuration(t.Value); err == nil {
if kv, err := h.keyValueSrvc.GetString(conf.KeyLatestTotalTime); err == nil && kv != nil && kv.Value != "" {
if d, err := time.ParseDuration(kv.Value); err == nil {
totalHours = int(d.Hours())
}
}
if t, err := h.keyValueSrvc.GetString(conf.KeyLatestTotalUsers); err == nil && t != nil && t.Value != "" {
if d, err := strconv.Atoi(t.Value); err == nil {
if kv, err := h.keyValueSrvc.GetString(conf.KeyLatestTotalUsers); err == nil && kv != nil && kv.Value != "" {
if d, err := strconv.Atoi(kv.Value); err == nil {
totalUsers = d
}
}
if kv, err := h.keyValueSrvc.GetString(conf.KeyNewsbox); err == nil && kv != nil && kv.Value != "" {
json.NewDecoder(strings.NewReader(kv.Value)).Decode(&newsbox)
}
return &view.HomeViewModel{
Success: r.URL.Query().Get("success"),
Error: r.URL.Query().Get("error"),
TotalHours: totalHours,
TotalUsers: totalUsers,
Newsbox: &newsbox,
}
}

View File

@ -2,15 +2,15 @@ package routes
import (
"fmt"
"github.com/duke-git/lancet/v2/datetime"
"github.com/muety/wakapi/views"
"html/template"
"net/http"
"strings"
"github.com/duke-git/lancet/v2/datetime"
"github.com/muety/wakapi/config"
"github.com/muety/wakapi/models"
"github.com/muety/wakapi/utils"
"github.com/muety/wakapi/views"
)
type action func(w http.ResponseWriter, r *http.Request) (int, string, string)

View File

@ -93,6 +93,22 @@ body {
@apply font-semibold text-gray-400 hover:text-gray-300;
}
.newsbox {
@apply px-4 py-2 border-2 border-red-700 bg-gray-850 rounded-md text-white border-green-700;
}
.newsbox-default {
@apply border-green-700;
}
.newsbox-warning {
@apply border-yellow-600;
}
.newsbox-danger {
@apply border-red-700;
}
::-webkit-calendar-picker-indicator {
filter: invert(1);
cursor: pointer;

File diff suppressed because one or more lines are too long

Binary file not shown.

View File

@ -54,9 +54,13 @@ const docTemplate = `{
"last_7_days",
"30_days",
"last_30_days",
"6_months",
"last_6_months",
"12_months",
"last_12_months",
"any"
"last_year",
"any",
"all_time"
],
"type": "string",
"description": "Interval to aggregate data for",
@ -354,9 +358,13 @@ const docTemplate = `{
"last_7_days",
"30_days",
"last_30_days",
"6_months",
"last_6_months",
"12_months",
"last_12_months",
"any"
"last_year",
"any",
"all_time"
],
"type": "string",
"description": "Range interval identifier",
@ -445,9 +453,13 @@ const docTemplate = `{
"last_7_days",
"30_days",
"last_30_days",
"6_months",
"last_6_months",
"12_months",
"last_12_months",
"any"
"last_year",
"any",
"all_time"
],
"type": "string",
"description": "Range interval identifier",
@ -809,9 +821,13 @@ const docTemplate = `{
"last_7_days",
"30_days",
"last_30_days",
"6_months",
"last_6_months",
"12_months",
"last_12_months",
"any"
"last_year",
"any",
"all_time"
],
"type": "string",
"description": "Interval identifier",

View File

@ -46,9 +46,13 @@
"last_7_days",
"30_days",
"last_30_days",
"6_months",
"last_6_months",
"12_months",
"last_12_months",
"any"
"last_year",
"any",
"all_time"
],
"type": "string",
"description": "Interval to aggregate data for",
@ -346,9 +350,13 @@
"last_7_days",
"30_days",
"last_30_days",
"6_months",
"last_6_months",
"12_months",
"last_12_months",
"any"
"last_year",
"any",
"all_time"
],
"type": "string",
"description": "Range interval identifier",
@ -437,9 +445,13 @@
"last_7_days",
"30_days",
"last_30_days",
"6_months",
"last_6_months",
"12_months",
"last_12_months",
"any"
"last_year",
"any",
"all_time"
],
"type": "string",
"description": "Range interval identifier",
@ -801,9 +813,13 @@
"last_7_days",
"30_days",
"last_30_days",
"6_months",
"last_6_months",
"12_months",
"last_12_months",
"any"
"last_year",
"any",
"all_time"
],
"type": "string",
"description": "Interval identifier",

View File

@ -430,9 +430,13 @@ paths:
- last_7_days
- 30_days
- last_30_days
- 6_months
- last_6_months
- 12_months
- last_12_months
- last_year
- any
- all_time
in: path
name: interval
required: true
@ -623,9 +627,13 @@ paths:
- last_7_days
- 30_days
- last_30_days
- 6_months
- last_6_months
- 12_months
- last_12_months
- last_year
- any
- all_time
in: path
name: range
type: string
@ -686,9 +694,13 @@ paths:
- last_7_days
- 30_days
- last_30_days
- 6_months
- last_6_months
- 12_months
- last_12_months
- last_year
- any
- all_time
in: query
name: range
type: string
@ -925,9 +937,13 @@ paths:
- last_7_days
- 30_days
- last_30_days
- 6_months
- last_6_months
- 12_months
- last_12_months
- last_year
- any
- all_time
in: query
name: interval
type: string

View File

@ -2,6 +2,11 @@ module.exports = {
purge: {
enabled: true,
mode: 'all',
content: ['./views/*.tpl.html']
}
content: ['./views/*.tpl.html'],
safelist: [
'newsbox-default',
'newsbox-warning',
'newsbox-danger',
]
},
}

View File

@ -60,6 +60,8 @@ func ResolveIntervalTZ(interval *models.IntervalKey, tz *time.Location) (err err
from = now.AddDate(0, 0, -14)
case models.IntervalPast30Days:
from = now.AddDate(0, 0, -30)
case models.IntervalPast6Months:
from = now.AddDate(0, -6, 0)
case models.IntervalPast12Months:
from = now.AddDate(0, -12, 0)
case models.IntervalAny:

View File

@ -1 +1 @@
2.3.5
dev

View File

@ -1,6 +1,6 @@
<footer class="flex justify-between w-full text-center text-gray-500 text-xs mt-20">
<div class="text-xs font-mono font-semibold">
v{{ getVersion }} @ {{ getDbType }}
{{ getVersion }} @ {{ getDbType }}
</div>
<div class="font-semibold text-sm hidden sm:inline-block">
Made with &nbsp; <span class="iconify inline" data-icon="bi:heart-fill"></span> &nbsp; by <a href="https://muetsch.io" class="text-gray-400 hover:text-gray-300">Ferdinand Mütsch</a> as <a

View File

@ -9,7 +9,7 @@
{{ template "alerts.tpl.html" . }}
<div class="absolute flex top-0 right-0 mr-8 mt-10 py-2">
<div class="absolute flex top-0 right-0 mr-4 mt-10 py-2">
<div class="mx-1">
<a href="login" class="btn-primary">
<span class="iconify inline" data-icon="fluent:key-24-filled"></span> &nbsp;Login</a>
@ -18,6 +18,12 @@
<main class="mt-10 px-4 md:px-10 lg:px-24 flex-grow flex justify-center w-full">
<div class="flex flex-col text-white">
{{ if and .Newsbox .Newsbox.Text }}
<div class="mb-14 -mt-4 newsbox newsbox-{{ .Newsbox.Type }}">
{{ .Newsbox.Text | htmlSafe }}
</div>
{{ end }}
<h1 class="text-8xl font-semibold antialiased text-center mb-10 leading-snug">Keep Track of<br><span
class="text-green-700">Your</span> Coding Time</h1>
<p class="text-center text-gray-500 text-xl my-2">Wakapi is an open-source tool that helps you keep track of the

View File

@ -14,6 +14,7 @@
<a id="time-option-year" class="submenu-item hover:bg-gray-800 rounded p-1 text-right w-full text-gray-300 px-2 font-semibold text-sm" :href="intervalLink('year')" @click="state.showDropdownTimepicker = !state.showDropdownTimepicker" data-trigger-for="showDropdownTimepicker">This Year</a>
<a id="time-option-last_7_days" class="submenu-item hover:bg-gray-800 rounded p-1 text-right w-full text-gray-300 px-2 font-semibold text-sm" :href="intervalLink('last_7_days')" @click="state.showDropdownTimepicker = !state.showDropdownTimepicker" data-trigger-for="showDropdownTimepicker">Past 7 Days</a>
<a id="time-option-last_30_days" class="submenu-item hover:bg-gray-800 rounded p-1 text-right w-full text-gray-300 px-2 font-semibold text-sm" :href="intervalLink('last_30_days')" @click="state.showDropdownTimepicker = !state.showDropdownTimepicker" data-trigger-for="showDropdownTimepicker">Past 30 Days</a>
<a id="time-option-last_6_months" class="submenu-item hover:bg-gray-800 rounded p-1 text-right w-full text-gray-300 px-2 font-semibold text-sm" :href="intervalLink('last_6_months')" @click="state.showDropdownTimepicker = !state.showDropdownTimepicker" data-trigger-for="showDropdownTimepicker">Past 6 Months</a>
<a id="time-option-last_12_months" class="submenu-item hover:bg-gray-800 rounded p-1 text-right w-full text-gray-300 px-2 font-semibold text-sm" :href="intervalLink('last_12_months')" @click="state.showDropdownTimepicker = !state.showDropdownTimepicker" data-trigger-for="showDropdownTimepicker">Past 12 Months</a>
<a id="time-option-any" class="submenu-item hover:bg-gray-800 rounded p-1 text-right w-full text-gray-300 px-2 font-semibold text-sm" :href="intervalLink('any')" @click="state.showDropdownTimepicker = !state.showDropdownTimepicker" data-trigger-for="showDropdownTimepicker">All Time</a>
<hr class="my-2">