Stub for settings page & api
Signed-off-by: Michael Mayer <michael@liquidbytes.net>
This commit is contained in:
parent
8e1d872a7b
commit
0becb8a92d
2442
frontend/package-lock.json
generated
2442
frontend/package-lock.json
generated
File diff suppressed because it is too large
Load diff
|
@ -14,34 +14,34 @@
|
|||
"test-firefox": "testcafe firefox:headless --selector-timeout 5000 -S -s tests/screenshots tests/acceptance"
|
||||
},
|
||||
"dependencies": {
|
||||
"@babel/cli": "^7.6.0",
|
||||
"@babel/core": "^7.6.0",
|
||||
"@babel/plugin-transform-runtime": "^7.6.0",
|
||||
"@babel/polyfill": "^7.6.0",
|
||||
"@babel/preset-env": "^7.6.0",
|
||||
"@babel/register": "^7.6.0",
|
||||
"@fortawesome/fontawesome-free": "^5.10.2",
|
||||
"@types/leaflet": "^1.5.1",
|
||||
"@babel/cli": "^7.7.0",
|
||||
"@babel/core": "^7.7.2",
|
||||
"@babel/plugin-transform-runtime": "^7.6.2",
|
||||
"@babel/polyfill": "^7.7.0",
|
||||
"@babel/preset-env": "^7.7.1",
|
||||
"@babel/register": "^7.7.0",
|
||||
"@fortawesome/fontawesome-free": "^5.11.2",
|
||||
"@types/leaflet": "^1.5.5",
|
||||
"acorn": "^6.3.0",
|
||||
"ajv": "^6.10.2",
|
||||
"autoprefixer": "^9.6.1",
|
||||
"autoprefixer": "^9.7.1",
|
||||
"axios": "^0.19.0",
|
||||
"axios-mock-adapter": "^1.17.0",
|
||||
"babel-eslint": "^10.0.3",
|
||||
"babel-loader": "^8.0.6",
|
||||
"babel-plugin-istanbul": "^5.2.0",
|
||||
"browserslist": "^4.7.0",
|
||||
"browserslist": "^4.7.2",
|
||||
"chai": "^4.2.0",
|
||||
"chalk": "^2.4.2",
|
||||
"chart.js": "^2.5.0",
|
||||
"chrome-finder": "^1.0.5",
|
||||
"chart.js": "^2.9.2",
|
||||
"chrome-finder": "^1.0.6",
|
||||
"clean-webpack-plugin": "^3.0.0",
|
||||
"connect-history-api-fallback": "^1.3.0",
|
||||
"copy-webpack-plugin": "^5.0.4",
|
||||
"copy-webpack-plugin": "^5.0.5",
|
||||
"cross-env": "^5.2.1",
|
||||
"css-loader": "^2.1.1",
|
||||
"cssnano": "^4.1.10",
|
||||
"eslint": "^6.4.0",
|
||||
"eslint": "^6.6.0",
|
||||
"eslint-config-standard": "^13.0.1",
|
||||
"eslint-formatter-pretty": "^2.1.1",
|
||||
"eslint-friendly-formatter": "^4.0.1",
|
||||
|
@ -57,7 +57,7 @@
|
|||
"html-webpack-plugin": "^3.2.0",
|
||||
"http-proxy-middleware": "^0.19.1",
|
||||
"inject-loader": "^4.0.1",
|
||||
"karma": "^4.3.0",
|
||||
"karma": "^4.4.1",
|
||||
"karma-chrome-launcher": "^3.1.0",
|
||||
"karma-coverage-istanbul-reporter": "^2.1.0",
|
||||
"karma-htmlfile-reporter": "^0.3.8",
|
||||
|
@ -65,16 +65,16 @@
|
|||
"karma-verbose-reporter": "^0.0.6",
|
||||
"karma-webpack": "^4.0.2",
|
||||
"leaflet": "^1.5.1",
|
||||
"luxon": "^1.17.3",
|
||||
"luxon": "^1.21.1",
|
||||
"material-design-icons-iconfont": "^5.0.1",
|
||||
"mini-css-extract-plugin": "^0.7.0",
|
||||
"mocha": "^6.2.0",
|
||||
"moment-timezone": "^0.5.26",
|
||||
"mocha": "^6.2.2",
|
||||
"moment-timezone": "^0.5.27",
|
||||
"optimize-css-assets-webpack-plugin": "^5.0.3",
|
||||
"ora": "^3.4.0",
|
||||
"photoswipe": "^4.1.3",
|
||||
"pluralize": "^8.0.0",
|
||||
"postcss": "^7.0.18",
|
||||
"postcss": "^7.0.21",
|
||||
"postcss-browser-reporter": "^0.6.0",
|
||||
"postcss-import": "^12.0.1",
|
||||
"postcss-loader": "^3.0.0",
|
||||
|
@ -83,17 +83,18 @@
|
|||
"postcss-url": "^8.0.0",
|
||||
"pubsub-js": "^1.7.0",
|
||||
"puppeteer-core": "^1.20.0",
|
||||
"resolve-url-loader": "^3.1.0",
|
||||
"resolve-url-loader": "^3.1.1",
|
||||
"sass-loader": "^7.3.1",
|
||||
"sinon": "^7.4.2",
|
||||
"sinon": "^7.5.0",
|
||||
"style-loader": "^0.23.1",
|
||||
"sugarss": "^2.0.0",
|
||||
"svg-url-loader": "^2.3.3",
|
||||
"tar": "^4.4.10",
|
||||
"tar": "^4.4.13",
|
||||
"truncate": "^2.1.0",
|
||||
"url-loader": "^1.1.2",
|
||||
"vue": "^2.6.10",
|
||||
"vue-fullscreen": "^2.1.5",
|
||||
"vue-gettext": "^2.1.6",
|
||||
"vue-infinite-scroll": "^2.0.2",
|
||||
"vue-loader": "^14.2.4",
|
||||
"vue-luxon": "^0.7.0",
|
||||
|
@ -103,10 +104,10 @@
|
|||
"vue2-filters": "^0.6.1",
|
||||
"vue2-leaflet": "^2.2.1",
|
||||
"vuelidate": "^0.7.4",
|
||||
"vuetify": "^1.5.18",
|
||||
"webpack": "^4.40.2",
|
||||
"webpack-bundle-analyzer": "^3.5.0",
|
||||
"webpack-cli": "^3.3.8",
|
||||
"vuetify": "^1.5.21",
|
||||
"webpack": "^4.41.2",
|
||||
"webpack-bundle-analyzer": "^3.6.0",
|
||||
"webpack-cli": "^3.3.10",
|
||||
"webpack-hot-middleware": "^2.25.0",
|
||||
"webpack-md5-hash": "0.0.6",
|
||||
"webpack-merge": "^4.2.2"
|
||||
|
|
|
@ -17,6 +17,8 @@ import VueLuxon from "vue-luxon";
|
|||
import VueInfiniteScroll from "vue-infinite-scroll";
|
||||
import VueFullscreen from "vue-fullscreen";
|
||||
import VueFilters from "vue2-filters";
|
||||
import GetTextPlugin from "vue-gettext";
|
||||
import translations from "./i18n/translations.json";
|
||||
import { Settings } from "luxon";
|
||||
|
||||
// Initialize helpers
|
||||
|
@ -49,9 +51,11 @@ Vue.use(Vuetify, {
|
|||
},
|
||||
});
|
||||
|
||||
Settings.defaultLocale = "en";
|
||||
Vue.config.language = "en";
|
||||
Settings.defaultLocale = Vue.config.language;
|
||||
|
||||
// Register other VueJS plugins
|
||||
Vue.use(GetTextPlugin, {translations: translations, silent: false, defaultLanguage: Vue.config.language});
|
||||
Vue.use(VueLuxon);
|
||||
Vue.use(VueInfiniteScroll);
|
||||
Vue.use(VueFullscreen);
|
||||
|
|
5
frontend/src/i18n/translations.json
Normal file
5
frontend/src/i18n/translations.json
Normal file
|
@ -0,0 +1,5 @@
|
|||
{
|
||||
"en": {
|
||||
"theme": "Theme"
|
||||
}
|
||||
}
|
48
frontend/src/model/settings.js
Normal file
48
frontend/src/model/settings.js
Normal file
|
@ -0,0 +1,48 @@
|
|||
import Api from "common/api";
|
||||
|
||||
class Settings {
|
||||
constructor(values) {
|
||||
this.__originalValues = {};
|
||||
|
||||
if (values) {
|
||||
this.setValues(values);
|
||||
}
|
||||
}
|
||||
|
||||
setValues(values) {
|
||||
if(!values) return;
|
||||
|
||||
for(let key in values) {
|
||||
if(values.hasOwnProperty(key) && key !== "__originalValues") {
|
||||
this[key] = values[key];
|
||||
this.__originalValues[key] = values[key];
|
||||
}
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
getValues() {
|
||||
const result = {};
|
||||
|
||||
for(let key in this.__originalValues) {
|
||||
if(this.__originalValues.hasOwnProperty(key) && key !== "__originalValues") {
|
||||
result[key] = this[key];
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
load() {
|
||||
return Api.get("settings").then((response) => {
|
||||
return Promise.resolve(this.setValues(response.data));
|
||||
});
|
||||
}
|
||||
|
||||
save() {
|
||||
return Api.post("settings", this.getValues()).then((response) => Promise.resolve(this.setValues(response.data)));
|
||||
}
|
||||
}
|
||||
|
||||
export default Settings;
|
|
@ -13,7 +13,7 @@
|
|||
<v-btn
|
||||
:disabled="busy"
|
||||
color="blue-grey"
|
||||
class="white--text ml-0"
|
||||
class="white--text ml-0 mt-2"
|
||||
depressed
|
||||
@click.stop="startImport()"
|
||||
>
|
||||
|
|
|
@ -13,7 +13,7 @@
|
|||
<v-btn
|
||||
:disabled="busy"
|
||||
color="blue-grey"
|
||||
class="white--text ml-0"
|
||||
class="white--text ml-0 mt-2"
|
||||
depressed
|
||||
@click.stop="startIndexing()"
|
||||
>
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
<v-btn
|
||||
:disabled="busy"
|
||||
color="blue-grey"
|
||||
class="white--text ml-0"
|
||||
class="white--text ml-0 mt-2"
|
||||
depressed
|
||||
@click.stop="uploadDialog()"
|
||||
>
|
||||
|
@ -48,7 +48,7 @@
|
|||
},
|
||||
methods: {
|
||||
submit() {
|
||||
console.log("SUBMIT");
|
||||
// console.log("SUBMIT");
|
||||
},
|
||||
uploadDialog() {
|
||||
this.$refs.upload.click();
|
||||
|
|
|
@ -7,8 +7,10 @@
|
|||
</v-toolbar>
|
||||
|
||||
<v-container class="pt-5">
|
||||
<p>Please enter the admin password to proceed:</p>
|
||||
<v-form ref="form" autocomplete="off" class="p-form-login" dense>
|
||||
<p class="subheading">
|
||||
<span>Please enter the admin password to proceed...</span>
|
||||
</p>
|
||||
<v-form ref="form" autocomplete="off" class="p-form-login" @submit.prevent="login" dense>
|
||||
<v-text-field
|
||||
label="Password"
|
||||
color="grey"
|
||||
|
|
|
@ -1,29 +1,38 @@
|
|||
<template>
|
||||
<div>
|
||||
<v-toolbar flat color="blue-grey lighten-4">
|
||||
<v-toolbar-title>Not implemented yet</v-toolbar-title>
|
||||
|
||||
<v-spacer></v-spacer>
|
||||
</v-toolbar>
|
||||
|
||||
<v-container>
|
||||
<p>
|
||||
Issues labeled <a href="https://github.com/photoprism/photoprism/labels/help%20wanted">help wanted</a> /
|
||||
<a href="https://github.com/photoprism/photoprism/labels/easy">easy</a> can be good (first)
|
||||
contributions.
|
||||
Our <a href="https://github.com/photoprism/photoprism/wiki">Developer Guide</a> contains all information
|
||||
necessary to get you started.
|
||||
</p>
|
||||
</v-container>
|
||||
<div class="p-page p-page-settings">
|
||||
<v-tabs
|
||||
v-model="active"
|
||||
flat
|
||||
grow
|
||||
color="blue-grey lighten-4"
|
||||
slider-color="blue-grey darken-1"
|
||||
height="64"
|
||||
>
|
||||
<v-tab id="tab-upload">
|
||||
General
|
||||
</v-tab>
|
||||
<v-tab-item>
|
||||
<p-tab-general></p-tab-general>
|
||||
</v-tab-item>
|
||||
</v-tabs>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import tabGeneral from "pages/settings/general.vue";
|
||||
|
||||
export default {
|
||||
name: 'todo',
|
||||
data() {
|
||||
return {};
|
||||
name: 'p-page-settings',
|
||||
components: {
|
||||
'p-tab-general': tabGeneral,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
readonly: this.$config.getValue("readonly"),
|
||||
active: 0,
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
},
|
||||
methods: {}
|
||||
};
|
||||
</script>
|
||||
|
|
68
frontend/src/pages/settings/general.vue
Normal file
68
frontend/src/pages/settings/general.vue
Normal file
|
@ -0,0 +1,68 @@
|
|||
<template>
|
||||
<div class="p-tab p-tab-general">
|
||||
<v-container fluid>
|
||||
<v-form ref="form" class="p-form-settings" lazy-validation @submit.prevent="save" dense>
|
||||
<v-layout wrap align-center>
|
||||
<v-flex xs12 sm6 class="pr-3">
|
||||
<v-select
|
||||
:items="languages"
|
||||
label="Language"
|
||||
color="blue-grey"
|
||||
value="en"
|
||||
flat
|
||||
></v-select>
|
||||
</v-flex>
|
||||
|
||||
<v-flex xs12 sm6 class="pr-3">
|
||||
<v-select
|
||||
:items="themes"
|
||||
label="Theme"
|
||||
color="blue-grey"
|
||||
value=""
|
||||
flat
|
||||
></v-select>
|
||||
</v-flex>
|
||||
</v-layout>
|
||||
|
||||
<v-btn color="blue-grey"
|
||||
class="white--text ml-0 mt-2"
|
||||
depressed
|
||||
@click.stop="save">
|
||||
Save
|
||||
<v-icon right dark>save</v-icon>
|
||||
</v-btn>
|
||||
</v-form>
|
||||
</v-container>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Settings from "model/settings";
|
||||
|
||||
export default {
|
||||
name: 'p-tab-general',
|
||||
data() {
|
||||
return {
|
||||
readonly: this.$config.getValue("readonly"),
|
||||
active: 0,
|
||||
settings: new Settings(),
|
||||
list: {},
|
||||
themes: [{text: "Default", value: ""}],
|
||||
languages: [{text: "English", value: "en"}],
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
load() {
|
||||
this.settings.load().then((r) => { this.list = r.getValues(); });
|
||||
},
|
||||
save() {
|
||||
this.settings.save().then(() => {
|
||||
this.$alert.info("Settings saved");
|
||||
})
|
||||
},
|
||||
},
|
||||
created() {
|
||||
this.load();
|
||||
},
|
||||
};
|
||||
</script>
|
26
internal/api/settings.go
Normal file
26
internal/api/settings.go
Normal file
|
@ -0,0 +1,26 @@
|
|||
package api
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/photoprism/photoprism/internal/config"
|
||||
)
|
||||
|
||||
// GET /api/v1/settings
|
||||
func GetSettings(router *gin.RouterGroup, conf *config.Config) {
|
||||
router.GET("/settings", func(c *gin.Context) {
|
||||
result := conf.Settings()
|
||||
|
||||
c.JSON(http.StatusOK, result)
|
||||
})
|
||||
}
|
||||
|
||||
// POST /api/v1/settings
|
||||
func SaveSettings(router *gin.RouterGroup, conf *config.Config) {
|
||||
router.POST("/settings", func(c *gin.Context) {
|
||||
// TODO
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{"message": "saved"})
|
||||
})
|
||||
}
|
|
@ -542,6 +542,7 @@ func (c *Config) Init(ctx context.Context) error {
|
|||
return c.connectToDatabase(ctx)
|
||||
}
|
||||
|
||||
// Shutdown closes open database connections.
|
||||
func (c *Config) Shutdown() {
|
||||
if err := c.CloseDb(); err != nil {
|
||||
log.Errorf("could not close database connection: %s", err)
|
||||
|
@ -549,3 +550,8 @@ func (c *Config) Shutdown() {
|
|||
log.Info("closed database connection")
|
||||
}
|
||||
}
|
||||
|
||||
// Settings returns the current user settings.
|
||||
func (c *Config) Settings() *Settings {
|
||||
return &Settings{}
|
||||
}
|
||||
|
|
6
internal/config/settings.go
Normal file
6
internal/config/settings.go
Normal file
|
@ -0,0 +1,6 @@
|
|||
package config
|
||||
|
||||
type Settings struct {
|
||||
Theme string `json:"theme" yaml:"theme" flag:"theme"`
|
||||
Language string `json:"language" yaml:"language" flag:"language"`
|
||||
}
|
|
@ -46,6 +46,9 @@ func registerRoutes(router *gin.Engine, conf *config.Config) {
|
|||
api.DislikeAlbum(v1, conf)
|
||||
api.AlbumThumbnail(v1, conf)
|
||||
api.CreateAlbum(v1, conf)
|
||||
|
||||
api.GetSettings(v1, conf)
|
||||
api.SaveSettings(v1, conf)
|
||||
}
|
||||
|
||||
// Default HTML page (client-side routing implemented via Vue.js)
|
||||
|
|
Loading…
Reference in a new issue