-
Notifications
You must be signed in to change notification settings - Fork 39
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add admin web frontend for managing and curating dictionary data.
The dashboard is built with static HTML pages served by Go with AlpineJS lib for interacting with the DOM. There is no build system after this fiasco: https://nadh.in/blog/javascript-ecosystem-software-development-are-a-hot-mess/ The dashboard allows to: - Search entries. - Add / edit / delete entries. - Add / edit / delete definitions (relations) under entries. - Re-order (up/down) relationships under entries.
- Loading branch information
Showing
32 changed files
with
1,372 additions
and
10,259 deletions.
There are no files selected for viewing
This file was deleted.
Oops, something went wrong.
This file was deleted.
Oops, something went wrong.
This file was deleted.
Oops, something went wrong.
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,58 @@ | ||
{{ define "header" }} | ||
<!DOCTYPE html> | ||
<html lang="en"> | ||
<head> | ||
<title>{{ .Title }} {{ if .Title }}|{{ end}} dictmaker</title> | ||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> | ||
<link rel="stylesheet" type="text/css" href="/admin/static/grid.css"> | ||
<link rel="stylesheet" type="text/css" href="/admin/static/style.css"> | ||
|
||
<script defer src="/admin/static/main.js"></script> | ||
<script defer src="/admin/static/alpine.js"></script> | ||
</head> | ||
<body x-init="onLoad()" x-data="globalComponent()"> | ||
<template x-if="ready"> | ||
<div class="container wrap"> | ||
<header class="header"> | ||
<div class="row"> | ||
<div class="three columns logo"> | ||
<a href="/admin"><img src="/admin/static/logo.svg" alt="logo" /></a> | ||
<template x-if="Object.keys(loading).length > 0"><span class="loading"></span></template> | ||
</div> | ||
<nav class="eight columns"> | ||
<a href="" @click.prevent="onNewEntry">Add new</a> | ||
</nav> | ||
</div> | ||
</header> | ||
|
||
<form class="search" action="/admin/search" x-data="searchFormComponent()" @submit="onSearch"> | ||
<fieldset class="row"> | ||
<div class="column four"> | ||
<select name="from_lang" x-model="fromLang"> | ||
<option value="*guid">*GUID</option> | ||
<template x-for="[id, l] in Object.entries(config.languages)" :key="id"> | ||
<option :value="id" x-text="l.name" x-bind:selected="id === fromLang"></option> | ||
</template> | ||
</select> | ||
</div> | ||
<div class="column six"> | ||
<input type="text" name="query" x-model="query" placeholder="Search" required /> | ||
</div> | ||
<div class="column two"> | ||
<button class="button" type="submit" x-bind:disabled="loading['entries.search'] === true">Search</button> | ||
</div> | ||
</fieldset> | ||
</form> | ||
|
||
{{ end }} | ||
|
||
{{ define "footer" }} | ||
{{ template "entry" . }} | ||
</div> | ||
</template> | ||
<footer class="footer"> | ||
<a href="https://dict.press">DictPress <span x-text="config.version"></span></a> | ||
</footer> | ||
</body> | ||
</html> | ||
{{ end }} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,59 @@ | ||
{{ define "definition" }} | ||
<section x-data="definitionComponent()" | ||
@open-definition-form.window="onOpen" | ||
@keyup.escape.window="onClose" | ||
@close-definition-form.window="onClose"> | ||
<template x-if="isVisible"> | ||
<div class="panel relation-form"> | ||
<h3>Add definition</h3> | ||
<form @submit.prevent="onSave"> | ||
<div x-text="parent.content"></div> | ||
<div>↓</div> | ||
<div> | ||
<label>Definition</label> | ||
<textarea autofocus name="content" x-model="def.content"></textarea> | ||
</div> | ||
<br /> | ||
<template x-if="Object.keys(config.languages[parent.lang].types).length > 0"> | ||
<fieldset class="row"> | ||
<div class="column four"> | ||
<label>Language</label> | ||
<select name="lang" x-model="def.lang"> | ||
<template x-for="[id, l] in Object.entries(config.languages)" :key="id"> | ||
<option :value="id" x-text="l.name"></option> | ||
</template> | ||
</select> | ||
</div> | ||
|
||
<div class="column four"> | ||
<label>Relation types</label> | ||
<select name="lang" x-model="def.types" multiple> | ||
<template x-for="[id, typ] in Object.entries(config.languages[def.lang].types)" :key="id"> | ||
<option :value="id" x-text="typ"></option> | ||
</template> | ||
</select> | ||
<span class="help">Ctrl+click to select multiple values</span> | ||
</div> | ||
|
||
<div class="column four"> | ||
<label>Relation tags</label> | ||
<textarea name="tags" x-model="def.tags"></textarea> | ||
<span class="help">One per line</span> | ||
</div> | ||
</fieldset> | ||
</template> | ||
|
||
<fieldset> | ||
<label>Notes</label> | ||
<textarea name="notes" x-model="def.notes"></textarea> | ||
</fieldset> | ||
|
||
<fieldset class="buttons"> | ||
<button type="submit" class="button">Save</button> | ||
<button type="button" class="button button-outline" @click.prevent="onClose">Close</button> | ||
</fieldset> | ||
</form> | ||
</div> | ||
</template> | ||
</section> | ||
{{ end}} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,121 @@ | ||
{{ define "entry" }} | ||
<section x-data="entryComponent()" | ||
@open-entry-form.window="onOpen" | ||
@keyup.escape.window="onClose" | ||
@close-entry-form.window="onClose"> | ||
<template x-if="isVisible"> | ||
<div class="panel entry-form"> | ||
<h3 x-text="isNew ? 'New entry' : 'Edit entry'"></h3> | ||
<form @submit.prevent="onSave"> | ||
<fieldset class="row box"> | ||
<div class="column nine"> | ||
<label>Content</label> | ||
<textarea required autofocus name="content" x-ref="content" x-model="entry.content"></textarea> | ||
</div> | ||
<div class="column three"> | ||
<label>Initial</label> | ||
<input required type="text" name="initial" x-model="entry.initial" @focus="onFocusInitial" /> | ||
<span class="help">First letter</span> | ||
</div> | ||
</fieldset> | ||
<template x-if="isNew"> | ||
<fieldset> | ||
<label>GUID</label> | ||
<input type="text" name="guid" x-model="entry.guid" /> | ||
<span class="help">Leave empty to auto-generate.</span> | ||
</fieldset> | ||
</template> | ||
<p> | ||
<a href="" @click.prevent="onToggleOptions"> | ||
<span x-text="!isFormOpen ? 'More options +' : 'Hide options -'"></span> | ||
</a> | ||
</p> | ||
<template x-if="isFormOpen"> | ||
<div class="box"> | ||
<fieldset class="row"> | ||
<div class="column six"> | ||
<label>Language</label> | ||
<select name="lang" x-model="entry.lang" required> | ||
<template x-for="[id, l] in Object.entries(config.languages)" :key="id"> | ||
<option :value="id" x-text="l.name" x-bind:selected="id === entry.lang"></option> | ||
</template> | ||
</select> | ||
</div> | ||
<div class="column three"> | ||
<label>Status</label> | ||
<select name="status" x-model="entry.status" required> | ||
<option value="enabled">Enabled</option> | ||
<option value="disabled">Disabled</option> | ||
</select> | ||
</div> | ||
<div class="column three"> | ||
<label>Weight</label> | ||
<input type="number" step="0.01" x-model="entry.weight" /> | ||
<span class="help">Sort order. Leave as 0 to automatically set.</span> | ||
</div> | ||
</fieldset> | ||
|
||
<fieldset class="row"> | ||
<div class="column four"> | ||
<label>Phonetic notations</label> | ||
<textarea name="phones" x-model="entry.phones" cols="10"></textarea> | ||
<span class="help">One per line</span> | ||
</div> | ||
<div class="column four"> | ||
<label>Tags</label> | ||
<textarea name="tags" x-model="entry.tags"></textarea> | ||
<span class="help">One per line</span> | ||
</div> | ||
<div class="column four"> | ||
<label>Search tokens</label> | ||
<textarea name="tokens" x-model="entry.tokens"></textarea> | ||
<span class="help">TSVECTOR tokens. One per line. Leave empty to auto-generate.</span> | ||
</div> | ||
</fieldset> | ||
|
||
<fieldset> | ||
<label>Notes</label> | ||
<textarea name="notes" x-model="entry.notes"></textarea> | ||
</fieldset> | ||
</div> | ||
</template> | ||
<br /> | ||
<fieldset class="buttons"> | ||
<button class="button button-outline float-right" @click.prevent="onDeleteEntry" | ||
x-bind:disabled="loading['entries.delete'] === true">Delete</button> | ||
<button type="submit" class="button" | ||
x-bind:disabled="loading['entries.update'] === true || loading['entries.create'] === true">Save</button> | ||
<button class="button button-outline" @click.prevent="onClose">Close</button> | ||
</fieldset> | ||
</form> | ||
|
||
<template x-if="!isNew"> | ||
<ul class="no meta"> | ||
<li><label>Created</label> <span x-text="entry.created_at.slice(0, 16).replace('T', ' ')"></span></li> | ||
<li><label>Updated</label> <span x-text="entry.updated_at.slice(0, 16).replace('T', ' ')"></span></li> | ||
<li><label>GUID</label> <span x-text="entry.guid"></span></li> | ||
<li><label>ID</label> <span x-text="entry.id"></span></li> | ||
</ul> | ||
</template> | ||
|
||
<template x-if="!isNew && parentEntries.length > 0"> | ||
<div class="parents"> | ||
<h3>Parent entries (<span x-text="parentEntries.length"></span>)</h3> | ||
<ol class="relations"> | ||
<template x-for="r in parentEntries" :key="r.id"> | ||
<li class="rel"> | ||
<p> | ||
<a x-bind:href="makeURL({'guid': r.guid})" x-text="r.content" class="content"></a> | ||
<template x-if="r.phones.length > 0"> | ||
<span class="phones" x-text="r.phones.join(', ')"></span> | ||
</template> | ||
</p> | ||
</li> | ||
</template> | ||
</ol> | ||
</div> | ||
</template> | ||
</div> | ||
</template> | ||
</section> | ||
{{ end}} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
{{ define "index" }} | ||
{{ template "header" . }} | ||
|
||
<section class="home" x-data="homeComponent()" x-init="onLoad"> | ||
<template x-if="stats"> | ||
<div class="box stats row"> | ||
<div class="column"> | ||
<h2 x-text="formatNumber(stats.entries)"></h2> | ||
<p>Entries</p> | ||
</div> | ||
<div class="column"> | ||
<h2 x-text="formatNumber(stats.relations)"></h2> | ||
<p>Relations</p> | ||
</div> | ||
<div class="column"> | ||
<h2>Languages</h2> | ||
<ul class="no"> | ||
<template x-for="[l, num] in Object.entries(stats.languages)" :key="l"> | ||
<li> | ||
<label class="label" x-text="config.languages[l].name"></label> | ||
<span x-text="formatNumber(num)"></span> | ||
</li> | ||
</template> | ||
</ul> | ||
</div> | ||
</div> | ||
</template> | ||
</section> | ||
|
||
{{ template "footer" . }} | ||
{{ end}} |
This file was deleted.
Oops, something went wrong.
Binary file not shown.
This file was deleted.
Oops, something went wrong.
Oops, something went wrong.