feat(admin): update stylesheet
This commit is contained in:
25
server/ctrladmin/adminui/pages/change_avatar.tmpl
Normal file
25
server/ctrladmin/adminui/pages/change_avatar.tmpl
Normal file
@@ -0,0 +1,25 @@
|
||||
{{ component "layout" . }}
|
||||
{{ component "layout_user" . }}
|
||||
|
||||
{{ component "block" (props .
|
||||
"Icon" "user"
|
||||
"Name" (printf "changing %s's avatar" .SelectedUser.Name)
|
||||
) }}
|
||||
<div class="flex flex-col gap-2 items-end">
|
||||
{{ if ne (len .SelectedUser.Avatar) 0 }}
|
||||
<img class="h-[8rem] w-[8rem] object-cover" src="data:image/jpg;base64,{{ .SelectedUser.Avatar | base64 }}" />
|
||||
<form class="contents" action="{{ printf "/admin/delete_avatar_do?user=%s" .SelectedUser.Name | path }}" method="post">
|
||||
<input type="submit" value="delete avatar">
|
||||
</form>
|
||||
{{ end }}
|
||||
<form enctype="multipart/form-data" action="{{ printf "/admin/change_avatar_do?user=%s" .SelectedUser.Name | path }}" method="post">
|
||||
<div class="relative pointer-events-auto">
|
||||
<input class="auto-submit absolute opacity-0" name="avatar" type="file" accept="image/jpeg image/png image/gif" />
|
||||
<input type="button" value="choose file">
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
{{ end }}
|
||||
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
16
server/ctrladmin/adminui/pages/change_password.tmpl
Normal file
16
server/ctrladmin/adminui/pages/change_password.tmpl
Normal file
@@ -0,0 +1,16 @@
|
||||
{{ component "layout" . }}
|
||||
{{ component "layout_user" . }}
|
||||
|
||||
{{ component "block" (props .
|
||||
"Icon" "user"
|
||||
"Name" (printf "changing %s's password" .SelectedUser.Name)
|
||||
) }}
|
||||
<form class="flex flex-col gap-2 items-end" action="{{ printf "/admin/change_password_do?user=%s" .SelectedUser.Name | path }}" method="post">
|
||||
<input type="password" id="password_one" name="password_one" placeholder="new password">
|
||||
<input type="password" id="password_two" name="password_two" placeholder="verify new password">
|
||||
<input type="submit" value="change">
|
||||
</form>
|
||||
{{ end }}
|
||||
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
15
server/ctrladmin/adminui/pages/change_username.tmpl
Normal file
15
server/ctrladmin/adminui/pages/change_username.tmpl
Normal file
@@ -0,0 +1,15 @@
|
||||
{{ component "layout" . }}
|
||||
{{ component "layout_user" . }}
|
||||
|
||||
{{ component "block" (props .
|
||||
"Icon" "user"
|
||||
"Name" (printf "changing %s's username" .SelectedUser.Name)
|
||||
) }}
|
||||
<form class="flex flex-col md:flex-row gap-2 items-end" action="{{ printf "/admin/change_username_do?user=%s" .SelectedUser.Name | path }}" method="post">
|
||||
<input type="text" id="username" name="username" placeholder="new username">
|
||||
<input type="submit" value="change">
|
||||
</form>
|
||||
{{ end }}
|
||||
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
17
server/ctrladmin/adminui/pages/create_user.tmpl
Normal file
17
server/ctrladmin/adminui/pages/create_user.tmpl
Normal file
@@ -0,0 +1,17 @@
|
||||
{{ component "layout" . }}
|
||||
{{ component "layout_user" . }}
|
||||
|
||||
{{ component "block" (props .
|
||||
"Icon" "user"
|
||||
"Name" "creating new user"
|
||||
) }}
|
||||
<form class="flex flex-col gap-2 items-end" action="{{ path "/admin/create_user_do" }}" method="post">
|
||||
<input type="text" id="username" name="username" placeholder="username">
|
||||
<input type="password" id="password_one" name="password_one" placeholder="password">
|
||||
<input type="password" id="password_two" name="password_two" placeholder="verify password">
|
||||
<input type="submit" value="create">
|
||||
</form>
|
||||
{{ end }}
|
||||
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
15
server/ctrladmin/adminui/pages/delete_user.tmpl
Normal file
15
server/ctrladmin/adminui/pages/delete_user.tmpl
Normal file
@@ -0,0 +1,15 @@
|
||||
{{ component "layout" . }}
|
||||
{{ component "layout_user" . }}
|
||||
|
||||
{{ component "block" (props .
|
||||
"Icon" "user"
|
||||
"Name" (printf "deleting user %s" .SelectedUser.Name)
|
||||
"Desc" "are you sure? this will also delete their plays, playlists, starred, rated, etc."
|
||||
) }}
|
||||
<form class="inline-block" action="{{ printf "/admin/delete_user_do?user=%s" .SelectedUser.Name | path }}" method="post">
|
||||
<input type="submit" value="yes">
|
||||
</form>
|
||||
{{ end }}
|
||||
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
226
server/ctrladmin/adminui/pages/home.tmpl
Normal file
226
server/ctrladmin/adminui/pages/home.tmpl
Normal file
@@ -0,0 +1,226 @@
|
||||
{{ component "layout" . }}
|
||||
{{ component "layout_user" . }}
|
||||
|
||||
{{ component "block" (props .
|
||||
"Icon" "chart-pie"
|
||||
"Name" "stats"
|
||||
"Desc" "total items found in all watched folders"
|
||||
) }}
|
||||
<div class="grid grid-cols-[auto_min-content] gap-2 gap-x-5 text-right">
|
||||
<div class="text-gray-500">artists</div>
|
||||
<div class="font-bold">{{ .ArtistCount }}</div>
|
||||
<div class="text-gray-500">albums</div>
|
||||
<div class="font-bold">{{ .AlbumCount }}</div>
|
||||
<div class="text-gray-500">tracks</div>
|
||||
<div class="font-bold">{{ .TrackCount }}</div>
|
||||
</div>
|
||||
{{ end }}
|
||||
|
||||
{{ component "block" (props .
|
||||
"Icon" "users"
|
||||
"Name" "user management"
|
||||
"Desc" "manage user accounts for subsonic api and web interface access"
|
||||
) }}
|
||||
<div class="grid grid-cols-[repeat(3,auto)_max-content] md:grid-cols-[auto_repeat(5,min-content)] gap-2 gap-x-5 items-center text-right">
|
||||
{{ range $user := .AllUsers }}
|
||||
<div class="col-span-3 md:col-auto ellipsis">{{ $user.Name }}</div>
|
||||
<div class="text-gray-500 whitespace-nowrap">{{ $user.CreatedAt | date }}</div>
|
||||
{{ component "link" (props . "To" (printf "/admin/change_username?user=%s" $user.Name | path)) }}username{{ end }}
|
||||
{{ component "link" (props . "To" (printf "/admin/change_password?user=%s" $user.Name | path)) }}password{{ end }}
|
||||
{{ component "link" (props . "To" (printf "/admin/change_avatar?user=%s" $user.Name | path)) }}avatar{{ end }}
|
||||
{{ if $user.IsAdmin }}
|
||||
<div class="text-gray-500">delete<span class="hidden md:inline">…</span></div>
|
||||
{{ else }}
|
||||
{{ component "link" (props . "To" (printf "/admin/delete_user?user=%s" $user.Name | path)) }}delete{{ end }}
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
{{ if .User.IsAdmin }}
|
||||
<div class="col-span-full">{{ component "link" (props . "To" (path "/admin/create_user")) }}create new{{ end }}</div>
|
||||
{{ end }}
|
||||
</div>
|
||||
{{ end }}
|
||||
|
||||
{{ component "block" (props .
|
||||
"Icon" "folder-tree"
|
||||
"Name" "recent folders"
|
||||
) }}
|
||||
<div class="grid grid-cols-[1fr,auto] gap-x-3 gap-y-2 items-center justify-items-end">
|
||||
{{ if eq (len .RecentFolders) 0 }}
|
||||
<div class="col-span-full text-gray-500">no folders yet</div>
|
||||
{{ end }}
|
||||
{{ range $folder := .RecentFolders }}
|
||||
<div class="text-left ellipsis">{{ $folder.RightPath }}</div>
|
||||
<div class="text-gray-500" title="{{ $folder.ModifiedAt }}">{{ $folder.ModifiedAt | dateHuman }}</div>
|
||||
{{ end }}
|
||||
{{ if and (not .IsScanning) (.User.IsAdmin) }}
|
||||
{{ if not .LastScanTime.IsZero }}
|
||||
<p class="col-span-full text-gray-500" title="{{ .LastScanTime }}">scanned {{ .LastScanTime | dateHuman }}</p>
|
||||
{{ end }}
|
||||
<form class="col-span-full" action="{{ path "/admin/start_scan_inc_do" }}" method="post">
|
||||
<input type="submit" title="start a incremental scan" value="scan now">
|
||||
</form>
|
||||
{{ end }}
|
||||
{{ if .IsScanning }}<p class="text-green-500 col-span-full">scan in progress...</p>{{ end }}
|
||||
</div>
|
||||
{{ end }}
|
||||
|
||||
{{ component "block" (props .
|
||||
"Icon" "music"
|
||||
"Name" "transcoding device profiles"
|
||||
"Desc" "you can find your device's client name in the gonic logs. some common client names are <span class='italic text-gray-800'>DSub</span>, <span class='italic text-gray-800'>Jamstash</span>, <span class=\"italic text-gray-800\">Soundwaves</span>, or use <span class=\"italic text-gray-800\">*</span> as fallback rule for any client. see the \"transcoding profiles\" page on the wiki for more info"
|
||||
) }}
|
||||
<div class="grid grid-cols-[1fr_1fr_auto] gap-2 items-center justify-items-end">
|
||||
{{ range $pref := .TranscodePreferences }}
|
||||
{{ $formSuffix := kebabcase $pref.Client }}
|
||||
<div class="ellipsis">{{ $pref.Client }}</div>
|
||||
<div>{{ $pref.Profile }}</div>
|
||||
<form class="contents" action="{{ printf "/admin/delete_transcode_pref_do?client=%s" $pref.Client | path }}" method="post">
|
||||
<input type="submit" value="delete">
|
||||
</form>
|
||||
{{ end }}
|
||||
<form class="contents" action="{{ path "/admin/create_transcode_pref_do" }}" method="post">
|
||||
<input type="text" name="client" placeholder="client name">
|
||||
<select name="profile">
|
||||
{{ range $profile := .TranscodeProfiles }}<option value="{{ $profile }}">{{ $profile }}</option>{{ end }}
|
||||
</select>
|
||||
<input type="submit" value="save">
|
||||
</form>
|
||||
</div>
|
||||
{{ end }}
|
||||
|
||||
{{ component "block" (props .
|
||||
"Icon" "lastfm"
|
||||
"Name" "last.fm"
|
||||
"Desc" "scrobble to last.fm on a per user basis (the admin must set a global api key first)"
|
||||
) }}
|
||||
<div class="flex flex-col gap-2 items-end">
|
||||
{{ if .CurrentLastFMAPIKey }}
|
||||
{{ if .User.LastFMSession }}
|
||||
<p class="text-gray-500">current status <span class="font-bold text-green-500">linked</span></p>
|
||||
<form class="contents" action="{{ path "/admin/unlink_lastfm_do" }}" method="post">
|
||||
<input type="submit" value="unlink">
|
||||
</form>
|
||||
{{ else }}
|
||||
<p class="text-gray-500">current status <span class="font-bold text-red-400">unlinked</span></p>
|
||||
{{ $cbPath := path "/admin/link_lastfm_do" }}
|
||||
{{ $cbURL := printf "%s%s" .RequestRoot $cbPath }}
|
||||
<div>{{ component "link" (props . "To" (printf "https://www.last.fm/api/auth/?api_key=%s&cb=%s" .CurrentLastFMAPIKey $cbURL)) }}link{{ end }}</div>
|
||||
{{ end }}
|
||||
{{ else }}
|
||||
<p class="font-bold">api key not set</p>
|
||||
{{ if not .User.IsAdmin }}
|
||||
<p class="text-gray-500">please ask your admin to set it</p>
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
{{ if .User.IsAdmin }}
|
||||
<p>{{ component "link" (props . "To" (path "/admin/update_lastfm_api_key" )) }}update api key{{ end }}</p>
|
||||
{{ end }}
|
||||
</div>
|
||||
{{ end }}
|
||||
|
||||
{{ component "block" (props .
|
||||
"Icon" "brain"
|
||||
"Name" "listenbrainz"
|
||||
"Desc" "scrobble to listenbrainz and compatible sites on a per user basis"
|
||||
) }}
|
||||
<div class="grid grid-cols-[1fr_1fr_auto] gap-2 items-center justify-items-end">
|
||||
{{ if .User.ListenBrainzToken }}
|
||||
<p class="text-gray-500 col-span-full">current status <span class="font-bold text-green-500">linked</span></p>
|
||||
<form class="contents" action="{{ path "/admin/unlink_listenbrainz_do" }}" method="post">
|
||||
<input class="col-span-full" type="submit" value="unlink">
|
||||
</form>
|
||||
{{ else }}
|
||||
<p class="text-gray-500 col-span-full">current status <span class="font-bold text-red-400">unlinked</span></p>
|
||||
<form class="contents" action="{{ path "/admin/link_listenbrainz_do" }}" method="post">
|
||||
<input type="text" name="url" placeholder="server addr" value="{{ default .DefaultListenBrainzURL .User.ListenBrainzURL }}">
|
||||
<input type="text" name="token" placeholder="api key" value="{{ .User.ListenBrainzToken }}">
|
||||
<input type="submit" value="update">
|
||||
</form>
|
||||
{{ end }}
|
||||
</div>
|
||||
{{ end }}
|
||||
|
||||
{{ if .User.IsAdmin }}
|
||||
{{ component "block" (props .
|
||||
"Icon" "rss"
|
||||
"Name" "podcasts"
|
||||
"Desc" "you can add podcasts rss feeds here"
|
||||
) }}
|
||||
<div class="grid grid-cols-[auto_auto_min-content] md:grid-cols-[5fr_3fr_auto_auto] gap-2 items-center justify-items-end">
|
||||
{{ range $pref := .Podcasts }}
|
||||
<div class="ellipsis">{{ $pref.Title }}</div>
|
||||
<form class="contents" action="{{ printf "/admin/update_podcast_do?id=%d" $pref.ID | path }}" method="post">
|
||||
<select class="auto-submit" name="setting">
|
||||
<option value="latest" {{ if eq $pref.AutoDownload "latest" }}selected="selected"{{ end }}>download latest</option>
|
||||
<option value="none" {{ if eq (default "none" $pref.AutoDownload) "none" }}selected="selected"{{ end }}>no auto download</option>
|
||||
</select>
|
||||
</form>
|
||||
<form class="hidden md:contents" action="{{ printf "/admin/download_podcast_do?id=%d" $pref.ID | path }}" method="post">
|
||||
<input type="submit" value="download all">
|
||||
</form>
|
||||
<form class="contents" action="{{ printf "/admin/delete_podcast_do?id=%d" $pref.ID | path }}" method="post">
|
||||
<input type="submit" value="delete">
|
||||
</form>
|
||||
{{ end }}
|
||||
<form class="contents" action="{{ path "/admin/add_podcast_do" }}" method="post">
|
||||
<input class="md:col-start-2 col-span-2" type="text" name="feed" placeholder="rss feed url">
|
||||
<input type="submit" value="add new">
|
||||
</form>
|
||||
</div>
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
|
||||
{{ if .User.IsAdmin }}
|
||||
{{ component "block" (props .
|
||||
"Icon" "rss"
|
||||
"Name" "internet radio stations"
|
||||
"Desc" "you can add and update internet radio stations here"
|
||||
) }}
|
||||
<div class="grid grid-cols-[1fr_1fr_min-content_min-content] md:grid-cols-[1fr_1fr_1fr_auto_auto] gap-2 items-center justify-items-end">
|
||||
{{ range $pref := .InternetRadioStations }}
|
||||
<form class="contents" action="{{ printf "/admin/update_internet_radio_station_do?id=%d" $pref.ID | path }}" method="post">
|
||||
<input class="col-span-full md:col-auto" type="text" name="name" value={{ $pref.Name }}>
|
||||
<input type="text" name="streamURL" placeholder="stream url" value={{ $pref.StreamURL }}>
|
||||
<input type="text" name="homepageURL" placeholder="homepage url" value={{ $pref.HomepageURL }}>
|
||||
<input type="submit" value="update">
|
||||
</form>
|
||||
<form class="contents" action="{{ printf "/admin/delete_internet_radio_station_do?id=%d" $pref.ID | path }}" method="post">
|
||||
<input type="submit" value="delete">
|
||||
</form>
|
||||
{{ end }}
|
||||
<form class="contents" action="{{ path "/admin/add_internet_radio_station_do" }}" method="post">
|
||||
<input type="text" name="name" placeholder="name">
|
||||
<input type="text" name="streamURL" placeholder="stream url">
|
||||
<input type="text" name="homepageURL" placeholder="homepage url">
|
||||
<input class="col-auto md:col-span-2" type="submit" value="add">
|
||||
</form>
|
||||
</div>
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
|
||||
{{ component "block" (props .
|
||||
"Icon" "list"
|
||||
"Name" "playlists"
|
||||
"Desc" "choose a local <span class='italic text-gray-800'>.m3u8</span> file containing paths to music files that gonic has scanned. paths should be absolute, prefixed by the <span class='italic text-gray-800'>music-path</span> option that you started gonic with. a playlist will be created from the file and available to subsonic clients"
|
||||
) }}
|
||||
{{ if eq (len .Playlists) 0 }}
|
||||
<span class="text-gray-500">no playlists yet</span>
|
||||
{{ end }}
|
||||
<div class="grid grid-cols-[auto_1fr_auto] md:grid-cols-[auto_repeat(3,min-content)] gap-x-3 gap-y-2 items-center justify-items-end">
|
||||
{{ range $i, $playlist := .Playlists }}
|
||||
<div class="text-right ellipsis">{{ $playlist.Name }}</div>
|
||||
<div class="text-gray-500 whitespace-nowrap">({{ $playlist.TrackCount }} tracks)</div>
|
||||
<div class="text-right text-gray-500 whitespace-nowrap hidden md:block" title="{{ $playlist.CreatedAt }}">{{ $playlist.CreatedAt | dateHuman }}</div>
|
||||
<form class="contents" action="{{ printf "/admin/delete_playlist_do?id=%d" $playlist.ID | path }}" method="post">
|
||||
<input type="submit" value="delete">
|
||||
</form>
|
||||
{{ end }}
|
||||
<form class="col-span-full relative pointer-events-auto" enctype="multipart/form-data" action="{{ path "/admin/upload_playlist_do" }}" method="post">
|
||||
<input class="auto-submit absolute opacity-0" name="playlist-files" type="file" multiple />
|
||||
<input type="button" value="choose m3u8">
|
||||
</form>
|
||||
</div>
|
||||
{{ end }}
|
||||
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
13
server/ctrladmin/adminui/pages/login.tmpl
Normal file
13
server/ctrladmin/adminui/pages/login.tmpl
Normal file
@@ -0,0 +1,13 @@
|
||||
{{ component "layout" . }}
|
||||
{{ component "block" (props .
|
||||
"Icon" "user"
|
||||
"Name" "login"
|
||||
"Desc" "if you are logging in as an admin, the default credentials can be found in the readme"
|
||||
) }}
|
||||
<form class="flex flex-col md:flex-row gap-2 items-end" action="{{ path "/admin/login_do" }}" method="post">
|
||||
<input class="text-center" type="text" id="username" name="username" placeholder="username">
|
||||
<input class="text-center" type="password" id="password" name="password" placeholder="password">
|
||||
<input type="submit" value="login">
|
||||
</form>
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
3
server/ctrladmin/adminui/pages/not_found.tmpl
Normal file
3
server/ctrladmin/adminui/pages/not_found.tmpl
Normal file
@@ -0,0 +1,3 @@
|
||||
{{ component "layout" . }}
|
||||
<p class="bg-red-100 p-4 text-center">page not found</p>
|
||||
{{ end }}
|
||||
21
server/ctrladmin/adminui/pages/update_lastfm_api_key.tmpl
Normal file
21
server/ctrladmin/adminui/pages/update_lastfm_api_key.tmpl
Normal file
@@ -0,0 +1,21 @@
|
||||
{{ component "layout" . }}
|
||||
{{ component "layout_user" . }}
|
||||
|
||||
{{ component "block" (props .
|
||||
"Icon" "user"
|
||||
"Name" "update last.fm api keys"
|
||||
"Desc" "you can get an api key from last.fm here <a class='text-blue-500' href='https://www.last.fm/api/account/create' target='_blank' rel='noopener noreferrer'>here</a>. note, only the <span class='italic text-gray-800'>application name</span> field is required"
|
||||
) }}
|
||||
<div class="flex flex-col gap-2 items-end">
|
||||
<p class="text-gray-500">current key <span class="font-bold text-gray-800 italic">{{ default "not set" .CurrentLastFMAPIKey }}</span></p>
|
||||
<p class="text-gray-500">current secret <span class="font-bold text-gray-800 italic">{{ default "not set" .CurrentLastFMAPISecret }}</span></p>
|
||||
<form class="contents" action="{{ path "/admin/update_lastfm_api_key_do" }}" method="post">
|
||||
<input type="text" id="api_key" name="api_key" placeholder="new key">
|
||||
<input type="text" id="secret" name="secret" placeholder="new secret">
|
||||
<input type="submit" value="update">
|
||||
</form>
|
||||
</div>
|
||||
{{ end }}
|
||||
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
Reference in New Issue
Block a user