cleanup 1
This commit is contained in:
@@ -1,28 +0,0 @@
|
||||
{{ template "base" . }}
|
||||
{{ define "title" }}Activity{{ end }}
|
||||
{{ define "header" }}<a href="./activity">Activity</a>{{ end }}
|
||||
{{ define "content" }}
|
||||
<div class="overflow-x-auto">
|
||||
<div class="inline-block min-w-full overflow-hidden rounded shadow">
|
||||
<!-- Table Component - Utilizes Template "table-cell" -->
|
||||
{{ template "component/table" (dict
|
||||
"Columns" (slice "Document" "Time" "Duration" "Percent")
|
||||
"Keys" (slice "Document" "StartTime" "Duration" "EndPercentage")
|
||||
"Rows" .Data
|
||||
)
|
||||
}}
|
||||
</div>
|
||||
</div>
|
||||
{{ end }}
|
||||
<!-- Table Cell Definition -->
|
||||
{{ define "table-cell" }}
|
||||
{{ if eq .Name "Document" }}
|
||||
<a href="./documents/{{ .Data.DocumentID }}"
|
||||
>{{ .Data.Author }} - {{ .Data.Title }}</a
|
||||
>
|
||||
{{ else if eq .Name "EndPercentage" }}
|
||||
{{ index (fields .Data) .Name }}%
|
||||
{{ else }}
|
||||
{{ index (fields .Data) .Name }}
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
@@ -1,50 +0,0 @@
|
||||
<div class="w-full relative">
|
||||
<div
|
||||
class="flex gap-4 w-full h-full p-4 shadow-lg bg-white dark:bg-gray-700 rounded"
|
||||
>
|
||||
<div class="min-w-fit my-auto h-48 relative">
|
||||
<a href="./documents/{{ .ID }}">
|
||||
<img
|
||||
class="rounded object-cover h-full"
|
||||
src="./documents/{{ .ID }}/cover"
|
||||
/>
|
||||
</a>
|
||||
</div>
|
||||
<div class="flex flex-col justify-around dark:text-white w-full text-sm">
|
||||
<div class="inline-flex shrink-0 items-center">
|
||||
<div>
|
||||
<p class="text-gray-400">Title</p>
|
||||
<p class="font-medium">{{ or .Title "Unknown" }}</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="inline-flex shrink-0 items-center">
|
||||
<div>
|
||||
<p class="text-gray-400">Author</p>
|
||||
<p class="font-medium">{{ or .Author "Unknown" }}</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="inline-flex shrink-0 items-center">
|
||||
<div>
|
||||
<p class="text-gray-400">Progress</p>
|
||||
<p class="font-medium">{{ .Percentage }}%</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="inline-flex shrink-0 items-center">
|
||||
<div>
|
||||
<p class="text-gray-400">Time Read</p>
|
||||
<p class="font-medium">{{ niceSeconds .TotalTimeSeconds }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="absolute flex flex-col gap-2 right-4 bottom-4 text-gray-500 dark:text-gray-400"
|
||||
>
|
||||
<a href="./activity?document={{ .ID }}">{{ template "svg/activity" }}</a>
|
||||
{{ if .Filepath }}
|
||||
<a href="./documents/{{ .ID }}/file">{{ template "svg/download" }}</a>
|
||||
{{ else }}
|
||||
{{ template "svg/download" (dict "Disabled" true) }}
|
||||
{{ end }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -1,12 +0,0 @@
|
||||
{{ if .Link }}<a href="{{ .Link }}" {{ else }} <div {{ end }}class="w-full">
|
||||
<div class="flex gap-4 w-full p-4 bg-white shadow-lg dark:bg-gray-700 rounded">
|
||||
<div class="flex flex-col justify-around dark:text-white w-full text-sm">
|
||||
<p class="text-2xl font-bold text-black dark:text-white">{{ .Size }}</p>
|
||||
<p class="text-sm text-gray-400">{{ .Title }}</p>
|
||||
</div>
|
||||
</div>
|
||||
{{ if .Link }}
|
||||
</a>
|
||||
{{ else }}
|
||||
</div>
|
||||
{{ end }}
|
||||
@@ -1,32 +0,0 @@
|
||||
<div class="relative">
|
||||
<div class="text-gray-500 inline-flex gap-2 relative">
|
||||
<p>{{ .Title }}</p>
|
||||
<label class="my-auto cursor-pointer" for="edit-{{ .FormValue }}-button">
|
||||
{{ template "svg/edit" (dict "Size" 18) }}
|
||||
</label>
|
||||
<input
|
||||
type="checkbox"
|
||||
id="edit-{{ .FormValue }}-button"
|
||||
class="hidden css-button"
|
||||
/>
|
||||
<div
|
||||
class="absolute z-30 top-7 right-0 p-3 transition-all duration-200 bg-gray-200 rounded shadow-lg shadow-gray-500 dark:shadow-gray-900 dark:bg-gray-600"
|
||||
>
|
||||
<form
|
||||
method="POST"
|
||||
action="{{ .URL }}"
|
||||
class="flex flex-col gap-2 text-black dark:text-white text-sm"
|
||||
>
|
||||
<input
|
||||
type="text"
|
||||
id="{{ .FormValue }}"
|
||||
name="{{ .FormValue }}"
|
||||
value="{{ or .Value "N/A" }}"
|
||||
class="p-2 bg-gray-300 text-black dark:bg-gray-700 dark:text-white"
|
||||
/>
|
||||
{{ template "component/button" (dict "Title" "Save") }}
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<p class="font-medium text-lg">{{ or .Value "N/A" }}</p>
|
||||
</div>
|
||||
@@ -1,64 +0,0 @@
|
||||
<div class="w-full">
|
||||
<div class="flex flex-col justify-between h-full w-full px-4 py-6 bg-white shadow-lg dark:bg-gray-700 rounded">
|
||||
<div>
|
||||
<div class="flex justify-between">
|
||||
<p class="text-sm font-semibold text-gray-700 border-b border-gray-200 w-max dark:text-white dark:border-gray-500">
|
||||
{{ .Name }} Leaderboard
|
||||
</p>
|
||||
<div class="flex gap-2 text-xs text-gray-400 items-center">
|
||||
<label for="all-{{ .Name }}"
|
||||
class="cursor-pointer hover:text-black dark:hover:text-white">all</label>
|
||||
<label for="year-{{ .Name }}"
|
||||
class="cursor-pointer hover:text-black dark:hover:text-white">year</label>
|
||||
<label for="month-{{ .Name }}"
|
||||
class="cursor-pointer hover:text-black dark:hover:text-white">month</label>
|
||||
<label for="week-{{ .Name }}"
|
||||
class="cursor-pointer hover:text-black dark:hover:text-white">week</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<input type="radio"
|
||||
name="options-{{ .Name }}"
|
||||
id="all-{{ .Name }}"
|
||||
class="hidden peer/All"
|
||||
checked />
|
||||
<input type="radio"
|
||||
name="options-{{ .Name }}"
|
||||
id="year-{{ .Name }}"
|
||||
class="hidden peer/Year" />
|
||||
<input type="radio"
|
||||
name="options-{{ .Name }}"
|
||||
id="month-{{ .Name }}"
|
||||
class="hidden peer/Month" />
|
||||
<input type="radio"
|
||||
name="options-{{ .Name }}"
|
||||
id="week-{{ .Name }}"
|
||||
class="hidden peer/Week" />
|
||||
{{ range $key, $data := .Data }}
|
||||
<div class="flex items-end my-6 space-x-2 hidden peer-checked/{{ $key }}:block">
|
||||
{{ $length := len $data }}
|
||||
{{ if eq $length 0 }}
|
||||
<p class="text-5xl font-bold text-black dark:text-white">N/A</p>
|
||||
{{ else }}
|
||||
<p class="text-5xl font-bold text-black dark:text-white">{{ (index $data 0).UserID }}</p>
|
||||
{{ end }}
|
||||
</div>
|
||||
<div class="hidden dark:text-white peer-checked/{{ $key }}:block">
|
||||
{{ range $index, $item := $data }}
|
||||
{{ if lt $index 3 }}
|
||||
{{ if eq $index 0 }}
|
||||
<div class="flex items-center justify-between pt-2 pb-2 text-sm">
|
||||
{{ else }}
|
||||
<div class="flex items-center justify-between pt-2 pb-2 text-sm border-t border-gray-200">
|
||||
{{ end }}
|
||||
<div>
|
||||
<p>{{ $item.UserID }}</p>
|
||||
</div>
|
||||
<div class="flex items-end font-bold">{{ $item.Value }}</div>
|
||||
</div>
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
</div>
|
||||
{{ end}}
|
||||
</div>
|
||||
</div>
|
||||
@@ -1,147 +0,0 @@
|
||||
{{ if .Error }}
|
||||
<div class="absolute top-0 left-0 w-full h-full z-50">
|
||||
<div class="fixed top-0 left-0 bg-black opacity-50 w-screen h-screen"></div>
|
||||
<div
|
||||
class="relative flex flex-col gap-4 p-4 max-h-[95%] bg-white dark:bg-gray-800 overflow-scroll -translate-x-2/4 -translate-y-2/4 top-1/2 left-1/2 w-5/6 overflow-hidden shadow rounded"
|
||||
>
|
||||
<div class="text-center">
|
||||
<h3 class="text-lg font-bold leading-6 dark:text-gray-300">
|
||||
No Metadata Results Found
|
||||
</h3>
|
||||
</div>
|
||||
{{ template "component/button" (dict
|
||||
"Title" "Back to Document"
|
||||
"Type" "Link"
|
||||
"URL" (printf "/documents/%s" .ID)
|
||||
)
|
||||
}}
|
||||
</div>
|
||||
</div>
|
||||
{{ end }}
|
||||
{{ if .Metadata }}
|
||||
<div class="absolute top-0 left-0 w-full h-full z-50">
|
||||
<div class="fixed top-0 left-0 bg-black opacity-50 w-screen h-screen"></div>
|
||||
<div
|
||||
class="relative max-h-[95%] bg-white dark:bg-gray-800 overflow-scroll -translate-x-2/4 -translate-y-2/4 top-1/2 left-1/2 w-5/6 overflow-hidden shadow rounded"
|
||||
>
|
||||
<div class="py-5 text-center">
|
||||
<h3 class="text-lg font-bold leading-6 dark:text-gray-300">
|
||||
Metadata Results
|
||||
</h3>
|
||||
</div>
|
||||
<form
|
||||
id="metadata-save"
|
||||
method="POST"
|
||||
action="/documents/{{ .ID }}/edit"
|
||||
class="text-black dark:text-white border-b dark:border-black"
|
||||
>
|
||||
<dl>
|
||||
<div
|
||||
class="p-3 bg-gray-100 dark:bg-gray-900 grid grid-cols-3 gap-4 sm:px-6"
|
||||
>
|
||||
<dt class="my-auto font-medium text-gray-500">Cover</dt>
|
||||
<dd class="mt-1 text-sm sm:mt-0 sm:col-span-2">
|
||||
<img
|
||||
class="rounded object-fill h-32"
|
||||
src="https://books.google.com/books/content/images/frontcover/{{ .Metadata.SourceID }}?fife=w480-h690"
|
||||
/>
|
||||
</dd>
|
||||
</div>
|
||||
<div
|
||||
class="p-3 bg-white dark:bg-gray-800 grid grid-cols-3 gap-4 sm:px-6"
|
||||
>
|
||||
<dt class="my-auto font-medium text-gray-500">Title</dt>
|
||||
<dd class="mt-1 text-sm sm:mt-0 sm:col-span-2">
|
||||
{{ or .Metadata.Title "N/A" }}
|
||||
</dd>
|
||||
</div>
|
||||
<div
|
||||
class="p-3 bg-gray-100 dark:bg-gray-900 grid grid-cols-3 gap-4 sm:px-6"
|
||||
>
|
||||
<dt class="my-auto font-medium text-gray-500">Author</dt>
|
||||
<dd class="mt-1 text-sm sm:mt-0 sm:col-span-2">
|
||||
{{ or .Metadata.Author "N/A" }}
|
||||
</dd>
|
||||
</div>
|
||||
<div
|
||||
class="p-3 bg-white dark:bg-gray-800 grid grid-cols-3 gap-4 sm:px-6"
|
||||
>
|
||||
<dt class="my-auto font-medium text-gray-500">ISBN 10</dt>
|
||||
<dd class="mt-1 text-sm sm:mt-0 sm:col-span-2">
|
||||
{{ or .Metadata.ISBN10 "N/A" }}
|
||||
</dd>
|
||||
</div>
|
||||
<div
|
||||
class="p-3 bg-gray-100 dark:bg-gray-900 grid grid-cols-3 gap-4 sm:px-6"
|
||||
>
|
||||
<dt class="my-auto font-medium text-gray-500">ISBN 13</dt>
|
||||
<dd class="mt-1 text-sm sm:mt-0 sm:col-span-2">
|
||||
{{ or .Metadata.ISBN13 "N/A" }}
|
||||
</dd>
|
||||
</div>
|
||||
<div
|
||||
class="p-3 bg-white dark:bg-gray-800 sm:grid sm:grid-cols-3 sm:gap-4 px-6"
|
||||
>
|
||||
<dt class="my-auto font-medium text-gray-500">Description</dt>
|
||||
<dd class="max-h-[10em] overflow-scroll mt-1 sm:mt-0 sm:col-span-2">
|
||||
{{ or .Metadata.Description "N/A" }}
|
||||
</dd>
|
||||
</div>
|
||||
</dl>
|
||||
<div class="hidden">
|
||||
<input
|
||||
type="text"
|
||||
id="title"
|
||||
name="title"
|
||||
value="{{ .Metadata.Title }}"
|
||||
/>
|
||||
<input
|
||||
type="text"
|
||||
id="author"
|
||||
name="author"
|
||||
value="{{ .Metadata.Author }}"
|
||||
/>
|
||||
<input
|
||||
type="text"
|
||||
id="description"
|
||||
name="description"
|
||||
value="{{ .Metadata.Description }}"
|
||||
/>
|
||||
<input
|
||||
type="text"
|
||||
id="isbn_10"
|
||||
name="isbn_10"
|
||||
value="{{ .Metadata.ISBN10 }}"
|
||||
/>
|
||||
<input
|
||||
type="text"
|
||||
id="isbn_13"
|
||||
name="isbn_13"
|
||||
value="{{ .Metadata.ISBN13 }}"
|
||||
/>
|
||||
<input
|
||||
type="text"
|
||||
id="cover_gbid"
|
||||
name="cover_gbid"
|
||||
value="{{ .Metadata.SourceID }}"
|
||||
/>
|
||||
</div>
|
||||
</form>
|
||||
<div class="flex justify-end">
|
||||
<div class="flex gap-4 m-4 w-48">
|
||||
{{ template "component/button" (dict
|
||||
"Title" "Cancel"
|
||||
"Type" "Link"
|
||||
"URL" (printf "/documents/%s" .ID)
|
||||
)
|
||||
}}
|
||||
{{ template "component/button" (dict
|
||||
"Title" "Save"
|
||||
"FormName" "metadata-save"
|
||||
)
|
||||
}}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{{ end }}
|
||||
@@ -1,54 +0,0 @@
|
||||
<div class="w-full">
|
||||
<div
|
||||
class="relative w-full px-4 py-6 bg-white shadow-lg dark:bg-gray-700 rounded"
|
||||
>
|
||||
<p
|
||||
class="text-sm font-semibold text-gray-700 border-b border-gray-200 w-max dark:text-white dark:border-gray-500"
|
||||
>
|
||||
{{ if eq .Window "WEEK" }}
|
||||
Weekly Read Streak
|
||||
{{ else }}
|
||||
Daily Read Streak
|
||||
{{ end }}
|
||||
</p>
|
||||
<div class="flex items-end my-6 space-x-2">
|
||||
<p class="text-5xl font-bold text-black dark:text-white">
|
||||
{{ .CurrentStreak }}
|
||||
</p>
|
||||
</div>
|
||||
<div class="dark:text-white">
|
||||
<div
|
||||
class="flex items-center justify-between pb-2 mb-2 text-sm border-b border-gray-200"
|
||||
>
|
||||
<div>
|
||||
<p>
|
||||
{{ if eq .Window "WEEK" }}
|
||||
Current Weekly Streak
|
||||
{{ else }}
|
||||
Current Daily Streak
|
||||
{{ end }}
|
||||
</p>
|
||||
<div class="flex items-end text-sm text-gray-400">
|
||||
{{ .CurrentStreakStartDate }} ➞ {{ .CurrentStreakEndDate }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex items-end font-bold">{{ .CurrentStreak }}</div>
|
||||
</div>
|
||||
<div class="flex items-center justify-between pb-2 mb-2 text-sm">
|
||||
<div>
|
||||
<p>
|
||||
{{ if eq .Window "WEEK" }}
|
||||
Best Weekly Streak
|
||||
{{ else }}
|
||||
Best Daily Streak
|
||||
{{ end }}
|
||||
</p>
|
||||
<div class="flex items-end text-sm text-gray-400">
|
||||
{{ .MaxStreakStartDate }} ➞ {{ .MaxStreakEndDate }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex items-end font-bold">{{ .MaxStreak }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -1,32 +0,0 @@
|
||||
{{ $rows := .Rows }}
|
||||
{{ $cols := .Columns }}
|
||||
{{ $keys := .Keys }}
|
||||
<table class="min-w-full leading-normal bg-white dark:bg-gray-700 text-sm">
|
||||
<thead class="text-gray-800 dark:text-gray-400">
|
||||
<tr>
|
||||
{{ range $col := $cols }}
|
||||
<th
|
||||
class="p-3 font-normal text-left uppercase border-b border-gray-200 dark:border-gray-800"
|
||||
>
|
||||
{{ $col }}
|
||||
</th>
|
||||
{{ end }}
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="text-black dark:text-white">
|
||||
{{ if not $rows }}
|
||||
<tr>
|
||||
<td class="text-center p-3" colspan="4">No Results</td>
|
||||
</tr>
|
||||
{{ end }}
|
||||
{{ range $row := $rows }}
|
||||
<tr>
|
||||
{{ range $key := $keys }}
|
||||
<td class="p-3 border-b border-gray-200">
|
||||
{{ template "table-cell" (dict "Data" $row "Name" $key ) }}
|
||||
</td>
|
||||
{{ end }}
|
||||
</tr>
|
||||
{{ end }}
|
||||
</tbody>
|
||||
</table>
|
||||
@@ -1,28 +0,0 @@
|
||||
{{ template "base" . }}
|
||||
{{ define "title" }}Activity{{ end }}
|
||||
{{ define "header" }}<a href="./activity">Activity</a>{{ end }}
|
||||
{{ define "content" }}
|
||||
<div class="overflow-x-auto">
|
||||
<div class="inline-block min-w-full overflow-hidden rounded shadow">
|
||||
<!-- Table Component - Utilizes Template "table-cell" -->
|
||||
{{ template "component/table" (dict
|
||||
"Columns" (slice "Document" "Time" "Duration" "Percent")
|
||||
"Keys" (slice "Document" "StartTime" "Duration" "EndPercentage")
|
||||
"Rows" .Data
|
||||
)
|
||||
}}
|
||||
</div>
|
||||
</div>
|
||||
{{ end }}
|
||||
<!-- Table Cell Definition -->
|
||||
{{ define "table-cell" }}
|
||||
{{ if eq .Name "Document" }}
|
||||
<a href="./documents/{{ .Data.DocumentID }}"
|
||||
>{{ .Data.Author }} - {{ .Data.Title }}</a
|
||||
>
|
||||
{{ else if eq .Name "EndPercentage" }}
|
||||
{{ index (fields .Data) .Name }}%
|
||||
{{ else }}
|
||||
{{ index (fields .Data) .Name }}
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
@@ -1,254 +0,0 @@
|
||||
{{ template "base" . }}
|
||||
{{ define "title" }}Documents{{ end }}
|
||||
{{ define "header" }}<a href="/documents">Documents</a>{{ end }}
|
||||
{{ define "content" }}
|
||||
<div class="h-full w-full relative">
|
||||
<!-- Document Info -->
|
||||
<div
|
||||
class="h-full w-full overflow-scroll bg-white shadow-lg dark:bg-gray-700 rounded dark:text-white p-4"
|
||||
>
|
||||
<div
|
||||
class="flex flex-col gap-2 float-left w-44 md:w-60 lg:w-80 mr-4 mb-2 relative"
|
||||
>
|
||||
<label class="z-10 cursor-pointer" for="edit-cover-button">
|
||||
<img
|
||||
class="rounded object-fill w-full"
|
||||
src="/documents/{{ .Data.ID }}/cover"
|
||||
/>
|
||||
</label>
|
||||
{{ if .Data.Filepath }}
|
||||
<a
|
||||
href="/reader#id={{ .Data.ID }}&type=REMOTE"
|
||||
class="z-10 text-white bg-blue-700 hover:bg-blue-800 focus:ring-4 focus:ring-blue-300 font-medium rounded text-sm text-center py-1 dark:bg-blue-600 dark:hover:bg-blue-700 focus:outline-none dark:focus:ring-blue-800"
|
||||
>Read</a
|
||||
>
|
||||
{{ end }}
|
||||
<div class="flex flex-wrap-reverse justify-between z-20 gap-2 relative">
|
||||
<div class="min-w-[50%] md:mr-2">
|
||||
<div class="flex gap-1 text-sm">
|
||||
<p class="text-gray-500">ISBN-10:</p>
|
||||
<p class="font-medium">{{ or .Data.Isbn10 "N/A" }}</p>
|
||||
</div>
|
||||
<div class="flex gap-1 text-sm">
|
||||
<p class="text-gray-500">ISBN-13:</p>
|
||||
<p class="font-medium">{{ or .Data.Isbn13 "N/A" }}</p>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="flex grow justify-between my-auto text-gray-500 dark:text-gray-500"
|
||||
>
|
||||
<input
|
||||
type="checkbox"
|
||||
id="edit-cover-button"
|
||||
class="hidden css-button"
|
||||
/>
|
||||
<div
|
||||
class="absolute z-30 flex flex-col gap-2 top-0 left-0 p-3 transition-all duration-200 bg-gray-200 rounded shadow-lg shadow-gray-500 dark:shadow-gray-900 dark:bg-gray-600"
|
||||
>
|
||||
<form
|
||||
method="POST"
|
||||
enctype="multipart/form-data"
|
||||
action="./{{ .Data.ID }}/edit"
|
||||
class="flex flex-col gap-2 w-72 text-black dark:text-white text-sm"
|
||||
>
|
||||
<input type="file" id="cover_file" name="cover_file" />
|
||||
{{ template "component/button" (dict "Title" "Upload Cover") }}
|
||||
</form>
|
||||
<form
|
||||
method="POST"
|
||||
action="./{{ .Data.ID }}/edit"
|
||||
class="flex flex-col gap-2 w-72 text-black dark:text-white text-sm"
|
||||
>
|
||||
<input
|
||||
type="checkbox"
|
||||
checked
|
||||
id="remove_cover"
|
||||
name="remove_cover"
|
||||
class="hidden"
|
||||
/>
|
||||
{{ template "component/button" (dict "Title" "Remove Cover") }}
|
||||
</form>
|
||||
</div>
|
||||
<div class="relative">
|
||||
<label for="delete-button" class="cursor-pointer"
|
||||
>{{ template "svg/delete" (dict "Size" 28) }}</label
|
||||
>
|
||||
<input
|
||||
type="checkbox"
|
||||
id="delete-button"
|
||||
class="hidden css-button"
|
||||
/>
|
||||
<div
|
||||
class="absolute z-30 bottom-7 left-5 p-3 transition-all duration-200 bg-gray-200 rounded shadow-lg shadow-gray-500 dark:shadow-gray-900 dark:bg-gray-600"
|
||||
>
|
||||
<form
|
||||
method="POST"
|
||||
action="./{{ .Data.ID }}/delete"
|
||||
class="text-black dark:text-white text-sm w-24"
|
||||
>
|
||||
{{ template "component/button" (dict "Title" "Delete") }}
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<a href="../activity?document={{ .Data.ID }}"
|
||||
>{{ template "svg/activity" (dict "Size" 28) }}</a
|
||||
>
|
||||
<div class="relative">
|
||||
<label for="search-button"
|
||||
>{{ template "svg/search" (dict "Size" 28) }}</label
|
||||
>
|
||||
<input
|
||||
type="checkbox"
|
||||
id="search-button"
|
||||
class="hidden css-button"
|
||||
/>
|
||||
<div
|
||||
class="absolute z-30 bottom-7 left-5 p-3 transition-all duration-200 bg-gray-200 rounded shadow-lg shadow-gray-500 dark:shadow-gray-900 dark:bg-gray-600"
|
||||
>
|
||||
<form
|
||||
method="POST"
|
||||
action="./{{ .Data.ID }}/identify"
|
||||
class="flex flex-col gap-2 text-black dark:text-white text-sm"
|
||||
>
|
||||
<input
|
||||
type="text"
|
||||
id="title"
|
||||
name="title"
|
||||
placeholder="Title"
|
||||
value="{{ or .Data.Title nil }}"
|
||||
class="p-2 bg-gray-300 text-black dark:bg-gray-700 dark:text-white"
|
||||
/>
|
||||
<input
|
||||
type="text"
|
||||
id="author"
|
||||
name="author"
|
||||
placeholder="Author"
|
||||
value="{{ or .Data.Author nil }}"
|
||||
class="p-2 bg-gray-300 text-black dark:bg-gray-700 dark:text-white"
|
||||
/>
|
||||
<input
|
||||
type="text"
|
||||
id="isbn"
|
||||
name="isbn"
|
||||
placeholder="ISBN 10 / ISBN 13"
|
||||
value="{{ or .Data.Isbn13 (or .Data.Isbn10 nil) }}"
|
||||
class="p-2 bg-gray-300 text-black dark:bg-gray-700 dark:text-white"
|
||||
/>
|
||||
{{ template "component/button" (dict "Title" "Identify") }}
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
{{ if .Data.Filepath }}
|
||||
<a href="./{{ .Data.ID }}/file"
|
||||
>{{ template "svg/download" (dict "Size" 28) }}</a
|
||||
>
|
||||
{{ else }}
|
||||
{{ template "svg/download" (dict "Size" 28 "Disabled" true) }}
|
||||
{{ end }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="grid sm:grid-cols-2 justify-between gap-4 pb-4">
|
||||
{{ template "component/key-val-edit" (dict
|
||||
"Title" "Title"
|
||||
"Value" .Data.Title
|
||||
"URL" (printf "./%s/edit" .Data.ID)
|
||||
"FormValue" "title"
|
||||
)
|
||||
}}
|
||||
{{ template "component/key-val-edit" (dict
|
||||
"Title" "Author"
|
||||
"Value" .Data.Author
|
||||
"URL" (printf "./%s/edit" .Data.ID)
|
||||
"FormValue" "author"
|
||||
)
|
||||
}}
|
||||
<div class="relative">
|
||||
<div class="text-gray-500 inline-flex gap-2 relative">
|
||||
<p>Time Read</p>
|
||||
<label class="my-auto" for="progress-info-button"
|
||||
>{{ template "svg/info" (dict "Size" 18) }}</label
|
||||
>
|
||||
<input
|
||||
type="checkbox"
|
||||
id="progress-info-button"
|
||||
class="hidden css-button"
|
||||
/>
|
||||
<div
|
||||
class="absolute z-30 top-7 right-0 p-3 transition-all duration-200 bg-gray-200 rounded shadow-lg shadow-gray-500 dark:shadow-gray-900 dark:bg-gray-600"
|
||||
>
|
||||
<div class="text-xs flex">
|
||||
<p class="text-gray-400 w-32">Seconds / Percent</p>
|
||||
<p class="font-medium dark:text-white">
|
||||
{{ .Data.SecondsPerPercent }}
|
||||
</p>
|
||||
</div>
|
||||
<div class="text-xs flex">
|
||||
<p class="text-gray-400 w-32">Words / Minute</p>
|
||||
<p class="font-medium dark:text-white">{{ .Data.Wpm }}</p>
|
||||
</div>
|
||||
<div class="text-xs flex">
|
||||
<p class="text-gray-400 w-32">Est. Time Left</p>
|
||||
<p class="font-medium dark:text-white whitespace-nowrap">
|
||||
{{ niceSeconds .TotalTimeLeftSeconds }}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<p class="font-medium text-lg">
|
||||
{{ niceSeconds .Data.TotalTimeSeconds }}
|
||||
</p>
|
||||
</div>
|
||||
<div>
|
||||
<p class="text-gray-500">Progress</p>
|
||||
<p class="font-medium text-lg">{{ .Data.Percentage }}%</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="relative">
|
||||
<div class="text-gray-500 inline-flex gap-2 relative">
|
||||
<p>Description</p>
|
||||
<label class="my-auto" for="edit-description-button"
|
||||
>{{ template "svg/edit" (dict "Size" 18) }}</label
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
<div class="relative font-medium text-justify hyphens-auto">
|
||||
<input
|
||||
type="checkbox"
|
||||
id="edit-description-button"
|
||||
class="hidden css-button"
|
||||
/>
|
||||
<div
|
||||
class="absolute h-full w-full min-h-[10em] z-30 top-1 right-0 gap-4 flex transition-all duration-200"
|
||||
>
|
||||
<img
|
||||
class="hidden md:block invisible rounded w-44 md:w-60 lg:w-80 object-fill"
|
||||
src="/documents/{{ .Data.ID }}/cover"
|
||||
/>
|
||||
<form
|
||||
method="POST"
|
||||
action="./{{ .Data.ID }}/edit"
|
||||
class="flex flex-col gap-2 w-full text-black bg-gray-200 rounded shadow-lg shadow-gray-500 dark:text-white dark:shadow-gray-900 dark:bg-gray-600 text-sm p-3"
|
||||
>
|
||||
<textarea
|
||||
type="text"
|
||||
id="description"
|
||||
name="description"
|
||||
class="h-full w-full p-2 bg-gray-300 text-black dark:bg-gray-700 dark:text-white"
|
||||
>
|
||||
{{ or .Data.Description "N/A" }}</textarea
|
||||
>
|
||||
{{ template "component/button" (dict "Title" "Save") }}
|
||||
</form>
|
||||
</div>
|
||||
<p>{{ or .Data.Description "N/A" }}</p>
|
||||
</div>
|
||||
</div>
|
||||
{{ template "component/metadata" (dict
|
||||
"ID" .Data.ID
|
||||
"Metadata" .Metadata
|
||||
"Error" .MetadataError
|
||||
)
|
||||
}}
|
||||
</div>
|
||||
{{ end }}
|
||||
@@ -1,99 +0,0 @@
|
||||
{{ template "base" . }}
|
||||
{{ define "title" }}Documents{{ end }}
|
||||
{{ define "header" }}<a href="./documents">Documents</a>{{ end }}
|
||||
{{ define "content" }}
|
||||
<div
|
||||
class="flex flex-col gap-2 grow p-4 mb-4 rounded shadow-lg bg-white dark:bg-gray-700 text-gray-500 dark:text-white"
|
||||
>
|
||||
<form
|
||||
class="flex gap-4 flex-col lg:flex-row"
|
||||
action="./documents"
|
||||
method="GET"
|
||||
>
|
||||
<div class="flex flex-col w-full grow">
|
||||
<div class="flex relative">
|
||||
<span
|
||||
class="inline-flex items-center px-3 border-t bg-white border-l border-b border-gray-300 text-gray-500 shadow-sm text-sm"
|
||||
>
|
||||
{{ template "svg/search2" (dict "Size" 15) }}
|
||||
</span>
|
||||
<input
|
||||
type="text"
|
||||
id="search"
|
||||
name="search"
|
||||
class="flex-1 appearance-none rounded-none border border-gray-300 w-full py-2 px-2 bg-white text-gray-700 placeholder-gray-400 shadow-sm text-base focus:outline-none focus:ring-2 focus:ring-purple-600 focus:border-transparent"
|
||||
placeholder="Search Author / Title"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="lg:w-60">
|
||||
{{ template "component/button" (dict
|
||||
"Title" "Search"
|
||||
"Variant" "Secondary"
|
||||
)
|
||||
}}
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<div class="grid grid-cols-1 gap-4 md:grid-cols-2 lg:grid-cols-3">
|
||||
{{ range $doc := .Data }}
|
||||
{{ template "component/document-card" $doc }}
|
||||
{{ end }}
|
||||
</div>
|
||||
<div class="w-full flex gap-4 justify-center mt-4 text-black dark:text-white">
|
||||
{{ if .PreviousPage }}
|
||||
<a
|
||||
href="./documents?page={{ .PreviousPage }}&limit={{ .PageLimit }}"
|
||||
class="bg-white shadow-lg dark:bg-gray-600 hover:bg-gray-400 font-medium rounded text-sm text-center p-2 w-24 dark:hover:bg-gray-700 focus:outline-none"
|
||||
>◄</a
|
||||
>
|
||||
{{ end }}
|
||||
{{ if .NextPage }}
|
||||
<a
|
||||
href="./documents?page={{ .NextPage }}&limit={{ .PageLimit }}"
|
||||
class="bg-white shadow-lg dark:bg-gray-600 hover:bg-gray-400 font-medium rounded text-sm text-center p-2 w-24 dark:hover:bg-gray-700 focus:outline-none"
|
||||
>►</a
|
||||
>
|
||||
{{ end }}
|
||||
</div>
|
||||
<div
|
||||
class="fixed bottom-6 right-6 rounded-full flex items-center justify-center"
|
||||
>
|
||||
<input type="checkbox" id="upload-file-button" class="hidden css-button" />
|
||||
<div
|
||||
class="absolute right-0 z-10 bottom-0 rounded p-4 bg-gray-800 dark:bg-gray-200 text-white dark:text-black w-72 text-sm flex flex-col gap-2"
|
||||
>
|
||||
<form
|
||||
method="POST"
|
||||
enctype="multipart/form-data"
|
||||
action="./documents"
|
||||
class="flex flex-col gap-2"
|
||||
>
|
||||
<input
|
||||
type="file"
|
||||
accept=".epub"
|
||||
id="document_file"
|
||||
name="document_file"
|
||||
/>
|
||||
<button
|
||||
class="font-medium px-2 py-1 text-gray-800 bg-gray-500 dark:text-white hover:bg-gray-100 dark:hover:bg-gray-800"
|
||||
type="submit"
|
||||
>
|
||||
Upload File
|
||||
</button>
|
||||
</form>
|
||||
<label for="upload-file-button">
|
||||
<div
|
||||
class="w-full text-center cursor-pointer font-medium mt-2 px-2 py-1 text-gray-800 bg-gray-500 dark:text-white hover:bg-gray-100 dark:hover:bg-gray-800"
|
||||
>
|
||||
Cancel Upload
|
||||
</div>
|
||||
</label>
|
||||
</div>
|
||||
<label
|
||||
class="w-16 h-16 bg-gray-800 dark:bg-gray-200 rounded-full flex items-center justify-center opacity-30 hover:opacity-100 transition-all duration-200 cursor-pointer"
|
||||
for="upload-file-button"
|
||||
>{{ template "svg/upload" (dict "Size" 34) }}</label
|
||||
>
|
||||
</div>
|
||||
{{ end }}
|
||||
@@ -1,109 +0,0 @@
|
||||
{{ template "base" . }}
|
||||
{{ define "title" }}Home{{ end }}
|
||||
{{ define "header" }}<a href="./">Home</a>{{ end }}
|
||||
{{ define "content" }}
|
||||
<div class="flex flex-col gap-4">
|
||||
<div class="w-full">
|
||||
<div class="relative w-full bg-white shadow-lg dark:bg-gray-700 rounded">
|
||||
<p
|
||||
class="absolute top-3 left-5 text-sm font-semibold text-gray-700 border-b border-gray-200 w-max dark:text-white dark:border-gray-500"
|
||||
>
|
||||
Daily Read Totals
|
||||
</p>
|
||||
{{ $data := (getSVGGraphData .Data.GraphData 800 70 ) }}
|
||||
<div class="relative">
|
||||
<svg
|
||||
viewBox="26 0 755 {{ $data.Height }}"
|
||||
preserveAspectRatio="none"
|
||||
width="100%"
|
||||
height="6em"
|
||||
>
|
||||
<!-- Bezier Line Graph -->
|
||||
<path
|
||||
fill="#316BBE"
|
||||
fill-opacity="0.5"
|
||||
stroke="none"
|
||||
d="{{ $data.BezierPath }} {{ $data.BezierFill }}"
|
||||
/>
|
||||
<path fill="none" stroke="#316BBE" d="{{ $data.BezierPath }}" />
|
||||
</svg>
|
||||
<div
|
||||
class="flex absolute w-full h-full top-0"
|
||||
style="width: calc(100%*31/30);
|
||||
transform: translateX(-50%);
|
||||
left: 50%"
|
||||
>
|
||||
{{ range $index, $item := $data.LinePoints }}
|
||||
<!-- Required for iOS "Hover" Events (onclick) -->
|
||||
<div
|
||||
onclick
|
||||
class="opacity-0 hover:opacity-100 w-full"
|
||||
style="background: linear-gradient(rgba(128, 128, 128, 0.5), rgba(128, 128, 128, 0.5)) no-repeat center/2px 100%"
|
||||
>
|
||||
<div
|
||||
class="flex flex-col items-center p-2 rounded absolute top-3 dark:text-white text-xs pointer-events-none"
|
||||
style="transform: translateX(-50%);
|
||||
background-color: rgba(128, 128, 128, 0.2);
|
||||
left: 50%"
|
||||
>
|
||||
<span>{{ (index $.Data.GraphData $index).Date }}</span>
|
||||
<span
|
||||
>{{ (index $.Data.GraphData $index).MinutesRead }}
|
||||
minutes</span
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
{{ end }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="grid grid-cols-2 gap-4 md:grid-cols-4">
|
||||
{{ template "component/info-card" (dict
|
||||
"Title" "Documents"
|
||||
"Size" .Data.DatabaseInfo.DocumentsSize
|
||||
"Link" "./documents"
|
||||
)
|
||||
}}
|
||||
{{ template "component/info-card" (dict
|
||||
"Title" "Activity Records"
|
||||
"Size" .Data.DatabaseInfo.ActivitySize
|
||||
"Link" "./activity"
|
||||
)
|
||||
}}
|
||||
{{ template "component/info-card" (dict
|
||||
"Title" "Progress Records"
|
||||
"Size" .Data.DatabaseInfo.ProgressSize
|
||||
"Link" "./progress"
|
||||
)
|
||||
}}
|
||||
{{ template "component/info-card" (dict
|
||||
"Title" "Devices"
|
||||
"Size" .Data.DatabaseInfo.DevicesSize
|
||||
)
|
||||
}}
|
||||
</div>
|
||||
<div class="grid grid-cols-1 gap-4 md:grid-cols-2">
|
||||
{{ range $item := .Data.Streaks }}
|
||||
{{ template "component/streak-card" $item }}
|
||||
{{ end }}
|
||||
</div>
|
||||
<div class="grid grid-cols-1 gap-4 md:grid-cols-2 lg:grid-cols-3">
|
||||
{{ template "component/leaderboard-card" (dict
|
||||
"Name" "WPM"
|
||||
"Data" .Data.UserStatistics.WPM
|
||||
)
|
||||
}}
|
||||
{{ template "component/leaderboard-card" (dict
|
||||
"Name" "Duration"
|
||||
"Data" .Data.UserStatistics.Duration
|
||||
)
|
||||
}}
|
||||
{{ template "component/leaderboard-card" (dict
|
||||
"Name" "Words"
|
||||
"Data" .Data.UserStatistics.Words
|
||||
)
|
||||
}}
|
||||
</div>
|
||||
</div>
|
||||
{{ end }}
|
||||
@@ -1,28 +0,0 @@
|
||||
{{ template "base" . }}
|
||||
{{ define "title" }}Progress{{ end }}
|
||||
{{ define "header" }}<a href="./progress">Progress</a>{{ end }}
|
||||
{{ define "content" }}
|
||||
<div class="overflow-x-auto">
|
||||
<div class="inline-block min-w-full overflow-hidden rounded shadow">
|
||||
<!-- Table Component - Utilizes Template "table-cell" -->
|
||||
{{ template "component/table" (dict
|
||||
"Columns" (slice "Document" "Device Name" "Percentage" "Created At")
|
||||
"Keys" (slice "Document" "DeviceName" "Percentage" "CreatedAt")
|
||||
"Rows" .Data
|
||||
)
|
||||
}}
|
||||
</div>
|
||||
</div>
|
||||
{{ end }}
|
||||
<!-- Table Cell Definition -->
|
||||
{{ define "table-cell" }}
|
||||
{{ if eq .Name "Document" }}
|
||||
<a href="./documents/{{ .Data.DocumentID }}"
|
||||
>{{ .Data.Author }} - {{ .Data.Title }}</a
|
||||
>
|
||||
{{ else if eq .Name "Percentage" }}
|
||||
{{ index (fields .Data) .Name }}%
|
||||
{{ else }}
|
||||
{{ index (fields .Data) .Name }}
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
@@ -1,157 +0,0 @@
|
||||
{{ template "base" . }}
|
||||
{{ define "title" }}Search{{ end }}
|
||||
{{ define "header" }}<a href="./search">Search</a>{{ end }}
|
||||
{{ define "content" }}
|
||||
<div class="w-full flex flex-col md:flex-row gap-4">
|
||||
<div class="flex flex-col gap-4 grow">
|
||||
<div
|
||||
class="flex flex-col gap-2 grow p-4 rounded shadow-lg bg-white dark:bg-gray-700 text-gray-500 dark:text-white"
|
||||
>
|
||||
<form class="flex gap-4 flex-col lg:flex-row" action="./search">
|
||||
<div class="flex flex-col w-full grow">
|
||||
<div class="flex relative">
|
||||
<span
|
||||
class="inline-flex items-center px-3 border-t bg-white border-l border-b border-gray-300 text-gray-500 shadow-sm text-sm"
|
||||
>
|
||||
{{ template "svg/search2" (dict "Size" 15) }}
|
||||
</span>
|
||||
<input
|
||||
type="text"
|
||||
id="query"
|
||||
name="query"
|
||||
class="flex-1 appearance-none rounded-none border border-gray-300 w-full py-2 px-4 bg-white text-gray-700 placeholder-gray-400 shadow-sm text-base focus:outline-none focus:ring-2 focus:ring-purple-600 focus:border-transparent"
|
||||
placeholder="Query"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex relative min-w-[12em]">
|
||||
<span
|
||||
class="inline-flex items-center px-3 border-t bg-white border-l border-b border-gray-300 text-gray-500 shadow-sm text-sm"
|
||||
>
|
||||
{{ template "svg/documents" (dict "Size" 15) }}
|
||||
</span>
|
||||
<select
|
||||
class="flex-1 appearance-none rounded-none border border-gray-300 w-full py-2 px-4 bg-white text-gray-700 placeholder-gray-400 shadow-sm text-base focus:outline-none focus:ring-2 focus:ring-purple-600 focus:border-transparent"
|
||||
id="source"
|
||||
name="source"
|
||||
>
|
||||
<option value="LibGen">Library Genesis</option>
|
||||
<option value="Annas Archive">Annas Archive</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="lg:w-60">
|
||||
{{ template "component/button" (dict
|
||||
"Title" "Search"
|
||||
"Variant" "Secondary"
|
||||
)
|
||||
}}
|
||||
</div>
|
||||
</form>
|
||||
{{ if .SearchErrorMessage }}
|
||||
<span class="text-red-400 text-xs">{{ .SearchErrorMessage }}</span>
|
||||
{{ end }}
|
||||
</div>
|
||||
<div class="inline-block min-w-full overflow-hidden rounded shadow">
|
||||
<table
|
||||
class="min-w-full leading-normal bg-white dark:bg-gray-700 text-sm md:text-sm"
|
||||
>
|
||||
<thead class="text-gray-800 dark:text-gray-400">
|
||||
<tr>
|
||||
<th
|
||||
scope="col"
|
||||
class="w-12 p-3 font-normal text-left uppercase border-b border-gray-200 dark:border-gray-800"
|
||||
></th>
|
||||
<th
|
||||
scope="col"
|
||||
class="p-3 font-normal text-left uppercase border-b border-gray-200 dark:border-gray-800"
|
||||
>
|
||||
Document
|
||||
</th>
|
||||
<th
|
||||
scope="col"
|
||||
class="p-3 font-normal text-left uppercase border-b border-gray-200 dark:border-gray-800"
|
||||
>
|
||||
Series
|
||||
</th>
|
||||
<th
|
||||
scope="col"
|
||||
class="p-3 font-normal text-left uppercase border-b border-gray-200 dark:border-gray-800"
|
||||
>
|
||||
Type
|
||||
</th>
|
||||
<th
|
||||
scope="col"
|
||||
class="p-3 font-normal text-left uppercase border-b border-gray-200 dark:border-gray-800"
|
||||
>
|
||||
Size
|
||||
</th>
|
||||
<th
|
||||
scope="col"
|
||||
class="p-3 hidden md:block font-normal text-left uppercase border-b border-gray-200 dark:border-gray-800"
|
||||
>
|
||||
Date
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="text-black dark:text-white">
|
||||
{{ if not .Data }}
|
||||
<tr>
|
||||
<td class="text-center p-3" colspan="6">No Results</td>
|
||||
</tr>
|
||||
{{ end }}
|
||||
{{ range $item := .Data }}
|
||||
<tr>
|
||||
<td
|
||||
class="p-3 border-b border-gray-200 text-gray-500 dark:text-gray-500"
|
||||
>
|
||||
<form action="./search" method="POST">
|
||||
<input
|
||||
class="hidden"
|
||||
type="text"
|
||||
id="source"
|
||||
name="source"
|
||||
value="{{ $.Source }}"
|
||||
/>
|
||||
<input
|
||||
class="hidden"
|
||||
type="text"
|
||||
id="title"
|
||||
name="title"
|
||||
value="{{ $item.Title }}"
|
||||
/>
|
||||
<input
|
||||
class="hidden"
|
||||
type="text"
|
||||
id="author"
|
||||
name="author"
|
||||
value="{{ $item.Author }}"
|
||||
/>
|
||||
<button name="id" value="{{ $item.ID }}">
|
||||
{{ template "svg/download" }}
|
||||
</button>
|
||||
</form>
|
||||
</td>
|
||||
<td class="p-3 border-b border-gray-200">
|
||||
{{ $item.Author }} -
|
||||
{{ $item.Title }}
|
||||
</td>
|
||||
<td class="p-3 border-b border-gray-200">
|
||||
<p>{{ or $item.Series "N/A" }}</p>
|
||||
</td>
|
||||
<td class="p-3 border-b border-gray-200">
|
||||
<p>{{ or $item.FileType "N/A" }}</p>
|
||||
</td>
|
||||
<td class="p-3 border-b border-gray-200">
|
||||
<p>{{ or $item.FileSize "N/A" }}</p>
|
||||
</td>
|
||||
<td class="hidden md:table-cell p-3 border-b border-gray-200">
|
||||
<p>{{ or $item.UploadDate "N/A" }}</p>
|
||||
</td>
|
||||
</tr>
|
||||
{{ end }}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{{ end }}
|
||||
Reference in New Issue
Block a user